diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-11-18 16:35:47 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-11-18 15:45:54 +0000 |
commit | 32f5a1c56531e4210bc4cf8d8c7825d66e081888 (patch) | |
tree | eeeec6822f4d738d8454525233fd0e2e3a659e6d /chromium/ui/ozone | |
parent | 99677208ff3b216fdfec551fbe548da5520cd6fb (diff) | |
download | qtwebengine-chromium-32f5a1c56531e4210bc4cf8d8c7825d66e081888.tar.gz |
BASELINE: Update Chromium to 87.0.4280.67
Change-Id: Ib157360be8c2ffb2c73125751a89f60e049c1d54
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/ui/ozone')
157 files changed, 5815 insertions, 1648 deletions
diff --git a/chromium/ui/ozone/BUILD.gn b/chromium/ui/ozone/BUILD.gn index 296c06ab333..60cd6f56a80 100644 --- a/chromium/ui/ozone/BUILD.gn +++ b/chromium/ui/ozone/BUILD.gn @@ -31,8 +31,8 @@ if (ozone_platform_headless) { ozone_platform_deps += [ "platform/headless" ] } -if (ozone_platform_gbm) { - ozone_platforms += [ "gbm" ] +if (ozone_platform_drm) { + ozone_platforms += [ "drm" ] ozone_platform_deps += [ "platform/drm:gbm" ] ozone_platform_test_deps += [ "platform/drm:gbm_unittests" ] } @@ -129,11 +129,11 @@ component("ozone_base") { # Everyone should depend on //ui/ozone instead except a handful of # things that would otherwise create a cycle. "//chromeos/system:system", + "//ui/base/ime/chromeos/*", "//ui/events/ozone/*", "//ui/ozone/common/*", - "//ui/ozone/public/mojom", "//ui/ozone/platform/*", - "//ui/base/ime/chromeos/*", + "//ui/ozone/public/mojom", ] configs += [ "//third_party/khronos:khronos_headers" ] @@ -300,6 +300,8 @@ test("ozone_unittests") { # backend. if (ozone_platform_x11) { test("ozone_x11_unittests") { + use_xvfb = true + deps = [ ":test_support" ] deps += [ "platform/x11:x11_unittests" ] @@ -314,3 +316,21 @@ buildflag_header("buildflags") { header = "buildflags.h" flags = [ "OZONE_PLATFORM_X11=$ozone_platform_x11" ] } + +group("unittests") { + testonly = true + + visibility += [ "*" ] + + deps = [ + ":ozone_unittests", + "//ui/ozone/gl:ozone_gl_unittests", + ] + + if (ozone_platform_x11) { + deps += [ + "//ui/ozone:ozone_x11_unittests", + "//ui/platform_window/x11:x11_unittests", + ] + } +} diff --git a/chromium/ui/ozone/ozone.gni b/chromium/ui/ozone/ozone.gni index 951a5b8f425..7a4976c9c05 100644 --- a/chromium/ui/ozone/ozone.gni +++ b/chromium/ui/ozone/ozone.gni @@ -4,7 +4,6 @@ import("//build/config/chromecast_build.gni") import("//build/config/ui.gni") -import("//build/toolchain/kythe.gni") import("//build/toolchain/toolchain.gni") declare_args() { @@ -14,6 +13,10 @@ declare_args() { # Select platforms automatically. Turn this off for manual control. ozone_auto_platforms = use_ozone + + # TODO(petermcneeley): Backwards compatiblity support for VM images. + # Remove when deprecated. (https://crbug.com/1122009) + ozone_platform_gbm = -1 } declare_args() { @@ -24,8 +27,8 @@ declare_args() { # Compile the 'cast' platform. ozone_platform_cast = false - # Compile the 'gbm' platform. - ozone_platform_gbm = false + # Compile the 'drm' platform. + ozone_platform_drm = false # Compile the 'headless' platform. ozone_platform_headless = false @@ -64,14 +67,8 @@ declare_args() { } } else if (is_chromeos) { ozone_platform = "x11" - ozone_platform_gbm = true + ozone_platform_drm = true ozone_platform_x11 = true - - # Enable the Ozone Wayland platform for ChromiumOS codesearch-gen bots - # so we get cross-references in codesearch. We can remove this once - # we can compile both x11 and Ozone on desktop Linux Chrome. - # TODO(crbug.com/1085700): Remove enable_kythe_annotations here. - ozone_platform_wayland = enable_kythe_annotations } else if (is_desktop_linux) { ozone_platform = "x11" ozone_platform_wayland = true @@ -84,6 +81,12 @@ declare_args() { ozone_platform_scenic = true } } + + # TODO(petermcneeley): Backwards compatiblity support for VM images. + # Remove when deprecated. (https://crbug.com/1122009) + if (ozone_platform_gbm != -1) { + ozone_platform_drm = ozone_platform_gbm + } } import(ozone_extra_path) @@ -94,9 +97,20 @@ _ozone_extra_directory = get_path_info(ozone_extra_path, "dir") ozone_external_platform_visibility = [ "$_ozone_extra_directory/*" ] if (is_a_target_toolchain) { - assert(use_ozone || !(ozone_platform_cast || ozone_platform_gbm || + assert(use_ozone || !(ozone_platform_cast || ozone_platform_drm || ozone_platform_headless || ozone_platform_x11 || ozone_platform_wayland || ozone_platform_windows || ozone_platform_scenic), "Must set use_ozone to select ozone platforms") } + +# TODO(petermcneeley): Backwards compatiblity support for VM images. +# Remove when deprecated. (https://crbug.com/1122009) + +assert(ozone_platform_gbm == -1 || ozone_platform_drm == ozone_platform_gbm) + +ozone_platform_gbm = ozone_platform_drm + +if (ozone_platform == "gbm") { + ozone_platform = "drm" +} diff --git a/chromium/ui/ozone/platform/drm/BUILD.gn b/chromium/ui/ozone/platform/drm/BUILD.gn index 01ed60608fd..11f2f10ce12 100644 --- a/chromium/ui/ozone/platform/drm/BUILD.gn +++ b/chromium/ui/ozone/platform/drm/BUILD.gn @@ -2,6 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//build/config/chromecast_build.gni") import("//build/config/linux/pkg_config.gni") import("//gpu/vulkan/features.gni") import("//ui/ozone/ozone.gni") @@ -17,13 +18,15 @@ declare_args() { source_set("gbm") { sources = [ - "client_native_pixmap_factory_gbm.cc", - "client_native_pixmap_factory_gbm.h", + "client_native_pixmap_factory_drm.cc", + "client_native_pixmap_factory_drm.h", "common/display_types.h", "common/drm_util.cc", "common/drm_util.h", "common/scoped_drm_types.cc", "common/scoped_drm_types.h", + "gpu/crtc_commit_request.cc", + "gpu/crtc_commit_request.h", "gpu/crtc_controller.cc", "gpu/crtc_controller.h", "gpu/drm_device.cc", @@ -74,8 +77,6 @@ source_set("gbm") { "gpu/hardware_display_plane.h", "gpu/hardware_display_plane_atomic.cc", "gpu/hardware_display_plane_atomic.h", - "gpu/hardware_display_plane_dummy.cc", - "gpu/hardware_display_plane_dummy.h", "gpu/hardware_display_plane_manager.cc", "gpu/hardware_display_plane_manager.h", "gpu/hardware_display_plane_manager_atomic.cc", @@ -112,8 +113,8 @@ source_set("gbm") { "host/host_cursor_proxy.h", "host/host_drm_device.cc", "host/host_drm_device.h", - "ozone_platform_gbm.cc", - "ozone_platform_gbm.h", + "ozone_platform_drm.cc", + "ozone_platform_drm.h", ] deps = [ @@ -150,10 +151,12 @@ source_set("gbm") { "//ui/platform_window", ] - data_deps = [ - "//third_party/angle:libEGL", - "//third_party/angle:libGLESv2", - ] + if (!is_chromecast) { + data_deps = [ + "//third_party/angle:libEGL", + "//third_party/angle:libGLESv2", + ] + } if (is_chromeos) { deps += [ "//ui/base/ime/chromeos" ] diff --git a/chromium/ui/ozone/platform/drm/client_native_pixmap_factory_gbm.cc b/chromium/ui/ozone/platform/drm/client_native_pixmap_factory_drm.cc index 8e363e69280..67b821625ad 100644 --- a/chromium/ui/ozone/platform/drm/client_native_pixmap_factory_gbm.cc +++ b/chromium/ui/ozone/platform/drm/client_native_pixmap_factory_drm.cc @@ -2,7 +2,7 @@ // 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/client_native_pixmap_factory_gbm.h" +#include "ui/ozone/platform/drm/client_native_pixmap_factory_drm.h" #include <utility> @@ -12,7 +12,7 @@ namespace ui { -gfx::ClientNativePixmapFactory* CreateClientNativePixmapFactoryGbm() { +gfx::ClientNativePixmapFactory* CreateClientNativePixmapFactoryDrm() { return gfx::CreateClientNativePixmapFactoryDmabuf(); } diff --git a/chromium/ui/ozone/platform/drm/client_native_pixmap_factory_gbm.h b/chromium/ui/ozone/platform/drm/client_native_pixmap_factory_drm.h index 05506d6db43..e0b4f9ee316 100644 --- a/chromium/ui/ozone/platform/drm/client_native_pixmap_factory_gbm.h +++ b/chromium/ui/ozone/platform/drm/client_native_pixmap_factory_drm.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_OZONE_PLATFORM_DRM_CLIENT_NATIVE_PIXMAP_FACTORY_GBM_H_ -#define UI_OZONE_PLATFORM_DRM_CLIENT_NATIVE_PIXMAP_FACTORY_GBM_H_ +#ifndef UI_OZONE_PLATFORM_DRM_CLIENT_NATIVE_PIXMAP_FACTORY_DRM_H_ +#define UI_OZONE_PLATFORM_DRM_CLIENT_NATIVE_PIXMAP_FACTORY_DRM_H_ namespace gfx { class ClientNativePixmapFactory; @@ -12,8 +12,8 @@ class ClientNativePixmapFactory; namespace ui { // Constructor hook for use in constructor_list.cc -gfx::ClientNativePixmapFactory* CreateClientNativePixmapFactoryGbm(); +gfx::ClientNativePixmapFactory* CreateClientNativePixmapFactoryDrm(); } // namespace ui -#endif // UI_OZONE_PLATFORM_DRM_CLIENT_NATIVE_PIXMAP_FACTORY_GBM_H_ +#endif // UI_OZONE_PLATFORM_DRM_CLIENT_NATIVE_PIXMAP_FACTORY_DRM_H_ diff --git a/chromium/ui/ozone/platform/drm/gpu/crtc_commit_request.cc b/chromium/ui/ozone/platform/drm/gpu/crtc_commit_request.cc new file mode 100644 index 00000000000..8127e1633f2 --- /dev/null +++ b/chromium/ui/ozone/platform/drm/gpu/crtc_commit_request.cc @@ -0,0 +1,56 @@ +// Copyright 2020 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/crtc_commit_request.h" + +namespace ui { + +CrtcCommitRequest::CrtcCommitRequest(uint32_t crtc_id, + uint32_t connector_id, + drmModeModeInfo mode, + HardwareDisplayPlaneList* plane_list, + DrmOverlayPlaneList overlays, + bool should_enable) + : should_enable_(should_enable), + crtc_id_(crtc_id), + connector_id_(connector_id), + mode_(mode), + plane_list_(plane_list), + overlays_(std::move(overlays)) { + DCHECK(!should_enable || DrmOverlayPlane::GetPrimaryPlane(overlays_)); +} + +CrtcCommitRequest::~CrtcCommitRequest() = default; + +CrtcCommitRequest::CrtcCommitRequest(const CrtcCommitRequest& other) + : should_enable_(other.should_enable_), + crtc_id_(other.crtc_id_), + connector_id_(other.connector_id_), + mode_(other.mode_), + plane_list_(other.plane_list_), + overlays_(DrmOverlayPlane::Clone(other.overlays_)) {} + +// static +CrtcCommitRequest CrtcCommitRequest::EnableCrtcRequest( + uint32_t crtc_id, + uint32_t connector_id, + drmModeModeInfo mode, + HardwareDisplayPlaneList* plane_list, + DrmOverlayPlaneList overlays) { + DCHECK(plane_list && !overlays.empty()); + + return CrtcCommitRequest(crtc_id, connector_id, mode, plane_list, + std::move(overlays), /*should_enable=*/true); +} + +// static +CrtcCommitRequest CrtcCommitRequest::DisableCrtcRequest( + uint32_t crtc_id, + uint32_t connector_id, + HardwareDisplayPlaneList* plane_list) { + return CrtcCommitRequest(crtc_id, connector_id, {}, plane_list, + DrmOverlayPlaneList(), /*should_enable=*/false); +} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/drm/gpu/crtc_commit_request.h b/chromium/ui/ozone/platform/drm/gpu/crtc_commit_request.h new file mode 100644 index 00000000000..ad8eab84e7f --- /dev/null +++ b/chromium/ui/ozone/platform/drm/gpu/crtc_commit_request.h @@ -0,0 +1,67 @@ +// Copyright 2020 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. + +#ifndef UI_OZONE_PLATFORM_DRM_GPU_CRTC_COMMIT_REQUEST_H_ +#define UI_OZONE_PLATFORM_DRM_GPU_CRTC_COMMIT_REQUEST_H_ + +#include <stddef.h> +#include <stdint.h> +#include <xf86drmMode.h> + +#include "ui/ozone/platform/drm/gpu/drm_device.h" +#include "ui/ozone/platform/drm/gpu/drm_overlay_plane.h" + +namespace ui { + +struct HardwareDisplayPlaneList; + +class CrtcCommitRequest; +using CommitRequest = std::vector<CrtcCommitRequest>; + +// Container holding information for a single CRTC that need to be modeset. +// TODO(markyacoub): PAGE_FLIP could re-use the same CrtcCommitRequest. The +// difference between MODESET and PAGE_FLIP are minimal. +class CrtcCommitRequest { + public: + CrtcCommitRequest(const CrtcCommitRequest& other); + ~CrtcCommitRequest(); + + static CrtcCommitRequest EnableCrtcRequest( + uint32_t crtc_id, + uint32_t connector_id, + drmModeModeInfo mode, + HardwareDisplayPlaneList* plane_list, + DrmOverlayPlaneList overlays); + + static CrtcCommitRequest DisableCrtcRequest( + uint32_t crtc_id, + uint32_t connector_id, + HardwareDisplayPlaneList* plane_list = nullptr); + + bool should_enable() const { return should_enable_; } + uint32_t crtc_id() const { return crtc_id_; } + uint32_t connector_id() const { return connector_id_; } + const drmModeModeInfo& mode() const { return mode_; } + HardwareDisplayPlaneList* plane_list() const { return plane_list_; } + const DrmOverlayPlaneList& overlays() const { return overlays_; } + + private: + CrtcCommitRequest(uint32_t crtc_id, + uint32_t connector_id, + drmModeModeInfo mode, + HardwareDisplayPlaneList* plane_list, + DrmOverlayPlaneList overlays, + bool should_enable); + + const bool should_enable_ = false; + const uint32_t crtc_id_ = 0; + const uint32_t connector_id_ = 0; + const drmModeModeInfo mode_ = {}; + HardwareDisplayPlaneList* plane_list_ = nullptr; + const DrmOverlayPlaneList overlays_; +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_DRM_GPU_CRTC_COMMIT_REQUEST_H_ diff --git a/chromium/ui/ozone/platform/drm/gpu/crtc_controller.cc b/chromium/ui/ozone/platform/drm/gpu/crtc_controller.cc index a7b0de83828..4aaa7b272ac 100644 --- a/chromium/ui/ozone/platform/drm/gpu/crtc_controller.cc +++ b/chromium/ui/ozone/platform/drm/gpu/crtc_controller.cc @@ -9,6 +9,7 @@ #include "base/logging.h" #include "base/time/time.h" #include "ui/gfx/presentation_feedback.h" +#include "ui/ozone/platform/drm/common/drm_util.h" #include "ui/ozone/platform/drm/gpu/drm_device.h" #include "ui/ozone/platform/drm/gpu/drm_dumb_buffer.h" #include "ui/ozone/platform/drm/gpu/drm_framebuffer.h" @@ -22,10 +23,11 @@ CrtcController::CrtcController(const scoped_refptr<DrmDevice>& drm, uint32_t connector) : drm_(drm), crtc_(crtc), - connector_(connector) {} + connector_(connector), + state_(drm->plane_manager()->GetCrtcStateForCrtcId(crtc)) {} CrtcController::~CrtcController() { - if (!is_disabled_) { + if (!is_disabled()) { const std::vector<std::unique_ptr<HardwareDisplayPlane>>& all_planes = drm_->plane_manager()->planes(); for (const auto& plane : all_planes) { @@ -34,55 +36,21 @@ CrtcController::~CrtcController() { plane->set_in_use(false); } } - - DisableCursor(); - drm_->plane_manager()->DisableModeset(crtc_, connector_); - } -} - -bool CrtcController::Modeset(const DrmOverlayPlane& plane, - const drmModeModeInfo& mode, - const ui::HardwareDisplayPlaneList& plane_list) { - if (!drm_->plane_manager()->Modeset(crtc_, - plane.buffer->opaque_framebuffer_id(), - connector_, mode, plane_list)) { - PLOG(ERROR) << "Failed to modeset: crtc=" << crtc_ - << " connector=" << connector_ - << " framebuffer_id=" << plane.buffer->opaque_framebuffer_id() - << " mode=" << mode.hdisplay << "x" << mode.vdisplay << "@" - << mode.vrefresh; - return false; } - - mode_ = mode; - is_disabled_ = false; - - // Hold modeset buffer until page flip. This fixes a crash on entering - // hardware mirror mode in some circumstances (bug 888553). - // TODO(spang): Fix this better by changing how mirrors are set up (bug - // 899352). - modeset_framebuffer_ = plane.buffer; - - return true; -} - -bool CrtcController::Disable() { - if (is_disabled_) - return true; - - is_disabled_ = true; - DisableCursor(); - return drm_->plane_manager()->DisableModeset(crtc_, connector_); } bool CrtcController::AssignOverlayPlanes(HardwareDisplayPlaneList* plane_list, - const DrmOverlayPlaneList& overlays) { - DCHECK(!is_disabled_); + const DrmOverlayPlaneList& overlays, + bool is_modesetting) { + // If we're in the process of modesetting, the CRTC is still disabled. + // Once the modeset is done, we expect it to be enabled. + DCHECK(is_modesetting || !is_disabled()); const DrmOverlayPlane* primary = DrmOverlayPlane::GetPrimaryPlane(overlays); - if (primary && !drm_->plane_manager()->ValidatePrimarySize(*primary, mode_)) { + if (primary && + !drm_->plane_manager()->ValidatePrimarySize(*primary, state_.mode)) { VLOG(2) << "Trying to pageflip a buffer with the wrong size. Expected " - << mode_.hdisplay << "x" << mode_.vdisplay << " got " + << ModeSize(state_.mode).ToString() << " got " << primary->buffer->size().ToString() << " for" << " crtc=" << crtc_ << " connector=" << connector_; return true; @@ -90,7 +58,6 @@ bool CrtcController::AssignOverlayPlanes(HardwareDisplayPlaneList* plane_list, if (!drm_->plane_manager()->AssignOverlayPlanes(plane_list, overlays, crtc_)) { - PLOG(ERROR) << "Failed to assign overlay planes for crtc " << crtc_; return false; } @@ -102,7 +69,7 @@ std::vector<uint64_t> CrtcController::GetFormatModifiers(uint32_t format) { } void CrtcController::SetCursor(uint32_t handle, const gfx::Size& size) { - if (is_disabled_) + if (is_disabled()) return; if (!drm_->SetCursor(crtc_, handle, size)) { PLOG(ERROR) << "drmModeSetCursor: device " << drm_->device_path().value() @@ -112,20 +79,9 @@ void CrtcController::SetCursor(uint32_t handle, const gfx::Size& size) { } void CrtcController::MoveCursor(const gfx::Point& location) { - if (is_disabled_) + if (is_disabled()) return; drm_->MoveCursor(crtc_, location); } -void CrtcController::OnPageFlipComplete() { - modeset_framebuffer_ = nullptr; -} - -void CrtcController::DisableCursor() { - if (!drm_->SetCursor(crtc_, 0, gfx::Size())) { - PLOG(ERROR) << "drmModeSetCursor: device " << drm_->device_path().value() - << " crtc " << crtc_ << " disable"; - } -} - } // namespace ui diff --git a/chromium/ui/ozone/platform/drm/gpu/crtc_controller.h b/chromium/ui/ozone/platform/drm/gpu/crtc_controller.h index 52dbd6c9458..0ecb830e19b 100644 --- a/chromium/ui/ozone/platform/drm/gpu/crtc_controller.h +++ b/chromium/ui/ozone/platform/drm/gpu/crtc_controller.h @@ -34,24 +34,15 @@ class CrtcController { uint32_t connector); ~CrtcController(); - drmModeModeInfo mode() const { return mode_; } + drmModeModeInfo mode() const { return state_.mode; } uint32_t crtc() const { return crtc_; } uint32_t connector() const { return connector_; } const scoped_refptr<DrmDevice>& drm() const { return drm_; } - bool is_disabled() const { return is_disabled_; } - - // Calls the appropriate Plane Manager to perform the initial modesetting - // operation using |plane| as the buffer for the primary plane. The CRTC - // configuration is specified by |mode|. - bool Modeset(const DrmOverlayPlane& plane, - const drmModeModeInfo& mode, - const ui::HardwareDisplayPlaneList& plane_list); - - // Disables the controller. - bool Disable(); + bool is_disabled() const { return !state_.properties.active.value; } bool AssignOverlayPlanes(HardwareDisplayPlaneList* plane_list, - const DrmOverlayPlaneList& planes); + const DrmOverlayPlaneList& planes, + bool is_modesetting); // Returns a vector of format modifiers for the given fourcc format // on this CRTCs primary plane. A format modifier describes the @@ -66,11 +57,7 @@ class CrtcController { void SetCursor(uint32_t handle, const gfx::Size& size); void MoveCursor(const gfx::Point& location); - void OnPageFlipComplete(); - private: - void DisableCursor(); - const scoped_refptr<DrmDevice> drm_; const uint32_t crtc_; @@ -78,13 +65,7 @@ class CrtcController { // TODO(dnicoara) Add support for hardware mirroring (multiple connectors). const uint32_t connector_; - drmModeModeInfo mode_ = {}; - - scoped_refptr<DrmFramebuffer> modeset_framebuffer_; - - // Keeps track of the CRTC state. If a surface has been bound, then the value - // is set to false. Otherwise it is true. - bool is_disabled_ = true; + const HardwareDisplayPlaneManager::CrtcState& state_; DISALLOW_COPY_AND_ASSIGN(CrtcController); }; diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_display.cc b/chromium/ui/ozone/platform/drm/gpu/drm_display.cc index 8530924e44a..4f84ef488cf 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_display.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_display.cc @@ -23,6 +23,7 @@ namespace ui { namespace { const char kContentProtection[] = "Content Protection"; +const char kHdcpContentType[] = "HDCP Content Type"; const char kPrivacyScreen[] = "privacy-screen"; @@ -31,11 +32,20 @@ struct ContentProtectionMapping { display::HDCPState state; }; +struct HdcpContentTypeMapping { + const char* name; + display::ContentProtectionMethod content_type; +}; + const ContentProtectionMapping kContentProtectionStates[] = { {"Undesired", display::HDCP_STATE_UNDESIRED}, {"Desired", display::HDCP_STATE_DESIRED}, {"Enabled", display::HDCP_STATE_ENABLED}}; +const HdcpContentTypeMapping kHdcpContentTypeStates[] = { + {"HDCP Type0", display::CONTENT_PROTECTION_METHOD_HDCP_TYPE_0}, + {"HDCP Type1", display::CONTENT_PROTECTION_METHOD_HDCP_TYPE_1}}; + // Converts |state| to the DRM value associated with the it. uint32_t GetContentProtectionValue(drmModePropertyRes* property, display::HDCPState state) { @@ -47,9 +57,31 @@ uint32_t GetContentProtectionValue(drmModePropertyRes* property, } } - for (int i = 0; i < property->count_enums; ++i) + for (int i = 0; i < property->count_enums; ++i) { if (name == property->enums[i].name) return i; + } + + NOTREACHED(); + return 0; +} + +// Converts |content_type| to the DRM value associated with the it. +uint32_t GetHdcpContentTypeValue( + drmModePropertyRes* property, + display::ContentProtectionMethod content_type) { + std::string name; + for (size_t i = 0; i < base::size(kHdcpContentTypeStates); ++i) { + if (kHdcpContentTypeStates[i].content_type == content_type) { + name = kHdcpContentTypeStates[i].name; + break; + } + } + + for (int i = 0; i < property->count_enums; ++i) { + if (name == property->enums[i].name) + return i; + } NOTREACHED(); return 0; @@ -73,10 +105,6 @@ std::string GetEnumNameForProperty(drmModeObjectProperties* property_values, return std::string(); } -gfx::Size GetDrmModeSize(const drmModeModeInfo& mode) { - return gfx::Size(mode.hdisplay, mode.vdisplay); -} - std::vector<drmModeModeInfo> GetDrmModeVector(drmModeConnector* connector) { std::vector<drmModeModeInfo> modes; for (int i = 0; i < connector->count_modes; ++i) @@ -99,11 +127,8 @@ void FillPowerFunctionValues(std::vector<display::GammaRampRGBEntry>* table, } // namespace -DrmDisplay::DrmDisplay(ScreenManager* screen_manager, - const scoped_refptr<DrmDevice>& drm) - : screen_manager_(screen_manager), - drm_(drm), - current_color_space_(gfx::ColorSpace::CreateSRGB()) {} +DrmDisplay::DrmDisplay(const scoped_refptr<DrmDevice>& drm) + : drm_(drm), current_color_space_(gfx::ColorSpace::CreateSRGB()) {} DrmDisplay::~DrmDisplay() = default; @@ -140,34 +165,9 @@ std::unique_ptr<display::DisplaySnapshot> DrmDisplay::Update( return params; } -bool DrmDisplay::Configure(const drmModeModeInfo* mode, - const gfx::Point& origin) { - VLOG(1) << "DRM configuring: device=" << drm_->device_path().value() - << " crtc=" << crtc_ << " connector=" << connector_->connector_id - << " origin=" << origin.ToString() - << " size=" << (mode ? GetDrmModeSize(*mode).ToString() : "0x0") - << " refresh_rate=" << (mode ? mode->vrefresh : 0) << "Hz"; - - if (mode) { - if (!screen_manager_->ConfigureDisplayController( - drm_, crtc_, connector_->connector_id, origin, *mode)) { - VLOG(1) << "Failed to configure: device=" << drm_->device_path().value() - << " crtc=" << crtc_ << " connector=" << connector_->connector_id; - return false; - } - } else { - if (!screen_manager_->DisableDisplayController(drm_, crtc_)) { - VLOG(1) << "Failed to disable device=" << drm_->device_path().value() - << " crtc=" << crtc_; - return false; - } - } - - origin_ = origin; - return true; -} - -bool DrmDisplay::GetHDCPState(display::HDCPState* state) { +bool DrmDisplay::GetHDCPState( + display::HDCPState* state, + display::ContentProtectionMethod* protection_method) { if (!connector_) return false; @@ -184,26 +184,83 @@ bool DrmDisplay::GetHDCPState(display::HDCPState* state) { connector_->connector_id, DRM_MODE_OBJECT_CONNECTOR)); std::string name = GetEnumNameForProperty(property_values.get(), hdcp_property.get()); - for (size_t i = 0; i < base::size(kContentProtectionStates); ++i) { + size_t i; + for (i = 0; i < base::size(kContentProtectionStates); ++i) { if (name == kContentProtectionStates[i].name) { *state = kContentProtectionStates[i].state; VLOG(3) << "HDCP state: " << *state << " (" << name << ")"; - return true; + break; + } + } + + if (i == base::size(kContentProtectionStates)) { + LOG(ERROR) << "Unknown content protection value '" << name << "'"; + return false; + } + + if (*state == display::HDCP_STATE_UNDESIRED) { + // ProtectionMethod doesn't matter if we don't have it desired/enabled. + *protection_method = display::CONTENT_PROTECTION_METHOD_NONE; + return true; + } + + ScopedDrmPropertyPtr content_type_property( + drm_->GetProperty(connector_.get(), kHdcpContentType)); + if (!content_type_property) { + // This won't exist if the driver doesn't support HDCP 2.2, so default it in + // that case. + VLOG(3) << "HDCP Content Type not supported, default to Type 0"; + *protection_method = display::CONTENT_PROTECTION_METHOD_HDCP_TYPE_0; + return true; + } + name = GetEnumNameForProperty(property_values.get(), + content_type_property.get()); + for (i = 0; i < base::size(kHdcpContentTypeStates); ++i) { + if (name == kHdcpContentTypeStates[i].name) { + *protection_method = kHdcpContentTypeStates[i].content_type; + VLOG(3) << "Content Protection Method: " << *protection_method << " (" + << name << ")"; + break; } } - LOG(ERROR) << "Unknown content protection value '" << name << "'"; - return false; + if (i == base::size(kHdcpContentTypeStates)) { + LOG(ERROR) << "Unknown HDCP content type value '" << name << "'"; + return false; + } + return true; } -bool DrmDisplay::SetHDCPState(display::HDCPState state) { +bool DrmDisplay::SetHDCPState( + display::HDCPState state, + display::ContentProtectionMethod protection_method) { if (!connector_) { return false; } + if (protection_method != display::CONTENT_PROTECTION_METHOD_NONE) { + ScopedDrmPropertyPtr content_type_property( + drm_->GetProperty(connector_.get(), kHdcpContentType)); + if (!content_type_property) { + // If the driver doesn't support HDCP 2.2, this won't exist. + if (protection_method & display::CONTENT_PROTECTION_METHOD_HDCP_TYPE_1) { + // We can't do this, since we can't specify the content type. + VLOG(3) + << "Cannot set HDCP Content Type 1 since driver doesn't support it"; + return false; + } + VLOG(3) << "HDCP Content Type not supported, default to Type 0"; + } else if (!drm_->SetProperty( + connector_->connector_id, content_type_property->prop_id, + GetHdcpContentTypeValue(content_type_property.get(), + protection_method))) { + // Failed setting HDCP Content Type. + return false; + } + } + ScopedDrmPropertyPtr hdcp_property( drm_->GetProperty(connector_.get(), kContentProtection)); - if (!hdcp_property) { PLOG(INFO) << "'" << kContentProtection << "' property doesn't exist."; return false; diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_display.h b/chromium/ui/ozone/platform/drm/gpu/drm_display.h index a658477fc2d..bac3ebce50d 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_display.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_display.h @@ -22,17 +22,15 @@ typedef struct _drmModeModeInfo drmModeModeInfo; namespace display { class DisplaySnapshot; struct GammaRampRGBEntry; -} +} // namespace display namespace ui { class DrmDevice; class HardwareDisplayControllerInfo; -class ScreenManager; class DrmDisplay { public: - DrmDisplay(ScreenManager* screen_manager, - const scoped_refptr<DrmDevice>& drm); + explicit DrmDisplay(const scoped_refptr<DrmDevice>& drm); ~DrmDisplay(); int64_t display_id() const { return display_id_; } @@ -45,9 +43,11 @@ class DrmDisplay { HardwareDisplayControllerInfo* info, size_t device_index); - bool Configure(const drmModeModeInfo* mode, const gfx::Point& origin); - bool GetHDCPState(display::HDCPState* state); - bool SetHDCPState(display::HDCPState state); + void SetOrigin(const gfx::Point origin) { origin_ = origin; } + bool GetHDCPState(display::HDCPState* state, + display::ContentProtectionMethod* protection_method); + bool SetHDCPState(display::HDCPState state, + display::ContentProtectionMethod protection_method); void SetColorMatrix(const std::vector<float>& color_matrix); void SetBackgroundColor(const uint64_t background_color); void SetGammaCorrection( @@ -63,8 +63,6 @@ class DrmDisplay { const std::vector<display::GammaRampRGBEntry>& degamma_lut, const std::vector<display::GammaRampRGBEntry>& gamma_lut); - ScreenManager* screen_manager_; // Not owned. - int64_t display_id_ = -1; const scoped_refptr<DrmDevice> drm_; uint32_t crtc_ = 0; diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_display_unittest.cc b/chromium/ui/ozone/platform/drm/gpu/drm_display_unittest.cc index c5aadaf7b11..b8af25613ab 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_display_unittest.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_display_unittest.cc @@ -14,7 +14,6 @@ #include "ui/ozone/platform/drm/gpu/hardware_display_plane.h" #include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h" #include "ui/ozone/platform/drm/gpu/mock_drm_device.h" -#include "ui/ozone/platform/drm/gpu/screen_manager.h" using ::testing::_; using ::testing::SizeIs; @@ -53,17 +52,11 @@ class MockHardwareDisplayPlaneManager : public HardwareDisplayPlaneManager { const std::vector<display::GammaRampRGBEntry>& degamma_lut, const std::vector<display::GammaRampRGBEntry>& gamma_lut)); - bool Modeset(uint32_t crtc_id, - uint32_t framebuffer_id, - uint32_t connector_id, - const drmModeModeInfo& mode, - const HardwareDisplayPlaneList& plane_list) override { - return false; - } - bool DisableModeset(uint32_t crtc_id, uint32_t connector) override { + bool Commit(CommitRequest commit_request, uint32_t flags) override { return false; } bool Commit(HardwareDisplayPlaneList* plane_list, + bool should_modeset, scoped_refptr<PageFlipRequest> page_flip_request, std::unique_ptr<gfx::GpuFence>* out_fence) override { return false; @@ -117,7 +110,7 @@ class DrmDisplayTest : public testing::Test { DrmDisplayTest() : mock_drm_device_(base::MakeRefCounted<MockDrmDevice>( std::make_unique<MockGbmDevice>())), - drm_display_(&screen_manager_, mock_drm_device_) {} + drm_display_(mock_drm_device_) {} MockHardwareDisplayPlaneManager* AddMockHardwareDisplayPlaneManager() { auto mock_hardware_display_plane_manager = @@ -132,7 +125,6 @@ class DrmDisplayTest : public testing::Test { base::test::TaskEnvironment env_; scoped_refptr<DrmDevice> mock_drm_device_; - ScreenManager screen_manager_; DrmDisplay drm_display_; }; diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc b/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc index 544df3618e8..61c721807f7 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc @@ -65,6 +65,33 @@ bool FindMatchingMode(const std::vector<drmModeModeInfo> modes, return false; } +bool FindModeForDisplay( + drmModeModeInfo* mode_ptr, + const display::DisplayMode& display_mode, + const std::vector<drmModeModeInfo>& modes, + const std::vector<std::unique_ptr<DrmDisplay>>& all_displays) { + bool mode_found = FindMatchingMode(modes, display_mode, mode_ptr); + if (!mode_found) { + // If the display doesn't have the mode natively, then lookup the mode + // from other displays and try using it on the current display (some + // displays support panel fitting and they can use different modes even + // if the mode isn't explicitly declared). + for (const auto& other_display : all_displays) { + mode_found = + FindMatchingMode(other_display->modes(), display_mode, mode_ptr); + if (mode_found) + break; + } + if (!mode_found) { + LOG(ERROR) << "Failed to find mode: size=" + << display_mode.size().ToString() + << " is_interlaced=" << display_mode.is_interlaced() + << " refresh_rate=" << display_mode.refresh_rate(); + } + } + return mode_found; +} + } // namespace DrmGpuDisplayManager::DrmGpuDisplayManager(ScreenManager* screen_manager, @@ -100,7 +127,7 @@ MovableDisplaySnapshots DrmGpuDisplayManager::GetDisplays() { displays_.push_back(std::move(*it)); old_displays.erase(it); } else { - displays_.push_back(std::make_unique<DrmDisplay>(screen_manager_, drm)); + displays_.push_back(std::make_unique<DrmDisplay>(drm)); } auto display_snapshot = @@ -139,77 +166,106 @@ void DrmGpuDisplayManager::RelinquishDisplayControl() { drm->DropMaster(); } -bool DrmGpuDisplayManager::ConfigureDisplay( - int64_t display_id, - const display::DisplayMode& display_mode, - const gfx::Point& origin) { - DrmDisplay* display = FindDisplay(display_id); - if (!display) { - LOG(ERROR) << "There is no display with ID " << display_id; - return false; - } +base::flat_map<int64_t, bool> DrmGpuDisplayManager::ConfigureDisplays( + const std::vector<display::DisplayConfigurationParams>& config_requests) { + base::flat_map<int64_t, bool> statuses; + std::vector<ScreenManager::ControllerConfigParams> controllers_to_configure; + + for (const auto& config : config_requests) { + int64_t display_id = config.id; + DrmDisplay* display = FindDisplay(display_id); + if (!display) { + LOG(ERROR) << "There is no display with ID " << display_id; + statuses.insert(std::make_pair(display_id, false)); + continue; + } - drmModeModeInfo mode; - bool mode_found = FindMatchingMode(display->modes(), display_mode, &mode); - if (!mode_found) { - // If the display doesn't have the mode natively, then lookup the mode from - // other displays and try using it on the current display (some displays - // support panel fitting and they can use different modes even if the mode - // isn't explicitly declared). - for (const auto& other_display : displays_) { - mode_found = - FindMatchingMode(other_display->modes(), display_mode, &mode); - if (mode_found) - break; + std::unique_ptr<drmModeModeInfo> mode_ptr = + config.mode ? std::make_unique<drmModeModeInfo>() : nullptr; + if (config.mode) { + if (!FindModeForDisplay(mode_ptr.get(), *config.mode.value(), + display->modes(), displays_)) { + statuses.insert(std::make_pair(display_id, false)); + continue; + } } - } - if (!mode_found) { - LOG(ERROR) << "Failed to find mode: size=" << display_mode.size().ToString() - << " is_interlaced=" << display_mode.is_interlaced() - << " refresh_rate=" << display_mode.refresh_rate(); - return false; + scoped_refptr<DrmDevice> drm = display->drm(); + + VLOG(1) << "DRM configuring: device=" << drm->device_path().value() + << " crtc=" << display->crtc() + << " connector=" << display->connector() + << " origin=" << config.origin.ToString() << " size=" + << (mode_ptr ? ModeSize(*(mode_ptr.get())).ToString() : "0x0") + << " refresh_rate=" << (mode_ptr ? mode_ptr->vrefresh : 0) << "Hz"; + + ScreenManager::ControllerConfigParams params( + display->display_id(), drm, display->crtc(), display->connector(), + config.origin, std::move(mode_ptr)); + controllers_to_configure.push_back(std::move(params)); } + if (controllers_to_configure.empty()) + return statuses; + if (clear_overlay_cache_callback_) clear_overlay_cache_callback_.Run(); - return display->Configure(&mode, origin); -} + auto config_statuses = + screen_manager_->ConfigureDisplayControllers(controllers_to_configure); + for (const auto& status : config_statuses) { + int64_t display_id = status.first; + bool success = status.second; + DrmDisplay* display = FindDisplay(display_id); + auto config = std::find_if( + config_requests.begin(), config_requests.end(), + [display_id](const auto& request) { return request.id == display_id; }); + + if (success) { + display->SetOrigin(config->origin); + } else { + if (config->mode) { + VLOG(1) << "Failed to enable device=" + << display->drm()->device_path().value() + << " crtc=" << display->crtc() + << " connector=" << display->connector(); + } else { + VLOG(1) << "Failed to disable device=" + << display->drm()->device_path().value() + << " crtc=" << display->crtc(); + } + } -bool DrmGpuDisplayManager::DisableDisplay(int64_t display_id) { - DrmDisplay* display = FindDisplay(display_id); - if (!display) { - LOG(ERROR) << "There is no display with ID " << display_id; - return false; + statuses.insert(std::make_pair(display_id, success)); } - if (clear_overlay_cache_callback_) - clear_overlay_cache_callback_.Run(); - - return display->Configure(nullptr, gfx::Point()); + return statuses; } -bool DrmGpuDisplayManager::GetHDCPState(int64_t display_id, - display::HDCPState* state) { +bool DrmGpuDisplayManager::GetHDCPState( + int64_t display_id, + display::HDCPState* state, + display::ContentProtectionMethod* protection_method) { DrmDisplay* display = FindDisplay(display_id); if (!display) { LOG(ERROR) << "There is no display with ID " << display_id; return false; } - return display->GetHDCPState(state); + return display->GetHDCPState(state, protection_method); } -bool DrmGpuDisplayManager::SetHDCPState(int64_t display_id, - display::HDCPState state) { +bool DrmGpuDisplayManager::SetHDCPState( + int64_t display_id, + display::HDCPState state, + display::ContentProtectionMethod protection_method) { DrmDisplay* display = FindDisplay(display_id); if (!display) { LOG(ERROR) << "There is no display with ID " << display_id; return false; } - return display->SetHDCPState(state); + return display->SetHDCPState(state, protection_method); } void DrmGpuDisplayManager::SetColorMatrix( @@ -280,15 +336,18 @@ DrmDisplay* DrmGpuDisplayManager::FindDisplay(int64_t display_id) { void DrmGpuDisplayManager::NotifyScreenManager( const std::vector<std::unique_ptr<DrmDisplay>>& new_displays, const std::vector<std::unique_ptr<DrmDisplay>>& old_displays) const { + ScreenManager::CrtcsWithDrmList controllers_to_remove; for (const auto& old_display : old_displays) { auto it = std::find_if(new_displays.begin(), new_displays.end(), DisplayComparator(old_display.get())); if (it == new_displays.end()) { - screen_manager_->RemoveDisplayController(old_display->drm(), - old_display->crtc()); + controllers_to_remove.emplace_back(old_display->crtc(), + old_display->drm()); } } + if (!controllers_to_remove.empty()) + screen_manager_->RemoveDisplayControllers(controllers_to_remove); for (const auto& new_display : new_displays) { auto it = std::find_if(old_displays.begin(), old_displays.end(), diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h b/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h index 6bc0d2472ea..2dbe2ba68a4 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h @@ -10,16 +10,19 @@ #include <vector> #include "base/callback.h" +#include "base/containers/flat_map.h" #include "base/macros.h" +#include "ui/display/types/display_configuration_params.h" #include "ui/display/types/display_constants.h" #include "ui/gfx/geometry/point.h" #include "ui/gfx/native_widget_types.h" #include "ui/ozone/platform/drm/common/display_types.h" +using drmModeModeInfo = struct _drmModeModeInfo; + namespace display { -class DisplayMode; struct GammaRampRGBEntry; -} +} // namespace display namespace gfx { class ColorSpace; @@ -49,16 +52,17 @@ class DrmGpuDisplayManager { bool TakeDisplayControl(); void RelinquishDisplayControl(); - bool ConfigureDisplay(int64_t id, - const display::DisplayMode& display_mode, - const gfx::Point& origin); - bool DisableDisplay(int64_t id); - bool GetHDCPState(int64_t display_id, display::HDCPState* state); - bool SetHDCPState(int64_t display_id, display::HDCPState state); + base::flat_map<int64_t, bool> ConfigureDisplays( + const std::vector<display::DisplayConfigurationParams>& config_requests); + bool GetHDCPState(int64_t display_id, + display::HDCPState* state, + display::ContentProtectionMethod* protection_method); + bool SetHDCPState(int64_t display_id, + display::HDCPState state, + display::ContentProtectionMethod protection_method); void SetColorMatrix(int64_t display_id, const std::vector<float>& color_matrix); - void SetBackgroundColor(int64_t display_id, - const uint64_t background_color); + void SetBackgroundColor(int64_t display_id, const uint64_t background_color); void SetGammaCorrection( int64_t display_id, const std::vector<display::GammaRampRGBEntry>& degamma_lut, diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_overlay_manager.cc b/chromium/ui/ozone/platform/drm/gpu/drm_overlay_manager.cc index 686e6633801..231d1728494 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_overlay_manager.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_overlay_manager.cc @@ -149,6 +149,15 @@ bool DrmOverlayManager::CanHandleCandidate( if (!gfx::IsNearestRectWithinDistance(candidate.display_rect, 0.01f)) return false; + // DRM supposedly supports subpixel source crop. However, according to + // drm_plane_funcs.update_plane, devices which don't support that are + // free to ignore the fractional part, and every device seems to do that as + // of 5.4. So reject candidates that require subpixel source crop. + gfx::RectF crop(candidate.crop_rect); + crop.Scale(candidate.buffer_size.width(), candidate.buffer_size.height()); + if (!gfx::IsNearestRectWithinDistance(crop, 0.01f)) + return false; + if (candidate.is_clipped && !candidate.clip_rect.Contains( gfx::ToNearestRect(candidate.display_rect))) { return false; diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_overlay_plane.cc b/chromium/ui/ozone/platform/drm/gpu/drm_overlay_plane.cc index 72a00b7dc1a..b88db11a91c 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_overlay_plane.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_overlay_plane.cc @@ -20,7 +20,7 @@ std::unique_ptr<gfx::GpuFence> CloneGpuFence( if (!gpu_fence) return nullptr; return std::make_unique<gfx::GpuFence>( - gfx::CloneHandleForIPC(gpu_fence->GetGpuFenceHandle())); + gpu_fence->GetGpuFenceHandle().Clone()); } } // namespace diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc b/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc index 3877bce8b68..6f20ce1f430 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc @@ -5,6 +5,7 @@ #include "ui/ozone/platform/drm/gpu/drm_overlay_validator.h" #include <drm_fourcc.h> +#include <xf86drm.h> #include <memory> #include <utility> @@ -29,9 +30,9 @@ namespace { -// Mode of size 6x4. -const drmModeModeInfo kDefaultMode = {0, 6, 0, 0, 0, 0, 4, 0, - 0, 0, 0, 0, 0, 0, {'\0'}}; +// Mode of size 12x8. +const drmModeModeInfo kDefaultMode = {0, 12, 0, 0, 0, 0, 8, 0, + 0, 0, 0, 0, 0, 0, {'\0'}}; const gfx::AcceleratedWidget kDefaultWidgetHandle = 1; constexpr uint32_t kCrtcIdBase = 1; @@ -77,6 +78,22 @@ class DrmOverlayValidatorTest : public testing::Test { return ui::DrmFramebuffer::AddFramebuffer(drm_, gbm_buffer.get(), size); } + bool ModesetController(ui::HardwareDisplayController* controller) { + ui::CommitRequest commit_request; + + ui::DrmOverlayPlane plane(CreateBuffer(), nullptr); + + controller->GetModesetProps(&commit_request, plane, kDefaultMode); + ui::CommitRequest request_for_update = commit_request; + bool status = drm_->plane_manager()->Commit(std::move(commit_request), + DRM_MODE_ATOMIC_ALLOW_MODESET); + controller->UpdateState( + /*enabled=*/true, + ui::DrmOverlayPlane::GetPrimaryPlane(request_for_update[0].overlays())); + + return status; + } + protected: struct PlaneState { std::vector<uint32_t> formats; @@ -213,8 +230,11 @@ void DrmOverlayValidatorTest::InitDrmStatesAndControllers( void DrmOverlayValidatorTest::SetupControllers() { screen_manager_ = std::make_unique<ui::ScreenManager>(); screen_manager_->AddDisplayController(drm_, kCrtcIdBase, kConnectorIdBase); - screen_manager_->ConfigureDisplayController( - drm_, kCrtcIdBase, kConnectorIdBase, gfx::Point(), kDefaultMode); + std::vector<ui::ScreenManager::ControllerConfigParams> controllers_to_enable; + controllers_to_enable.emplace_back( + 1 /*display_id*/, drm_, kCrtcIdBase, kConnectorIdBase, gfx::Point(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); drm_device_manager_ = std::make_unique<ui::DrmDeviceManager>(nullptr); @@ -418,9 +438,7 @@ TEST_F(DrmOverlayValidatorTest, ui::HardwareDisplayController* controller = window_->GetController(); controller->AddCrtc(std::make_unique<ui::CrtcController>( drm_.get(), kCrtcIdBase + 1, kConnectorIdBase + 1)); - ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr); - - EXPECT_TRUE(controller->Modeset(plane1, kDefaultMode)); + EXPECT_TRUE(ModesetController(controller)); gfx::RectF crop_rect = gfx::RectF(0, 0, 0.5, 0.5); overlay_params_.back().buffer_size = overlay_rect_.size(); @@ -463,9 +481,7 @@ TEST_F(DrmOverlayValidatorTest, ui::HardwareDisplayController* controller = window_->GetController(); controller->AddCrtc(std::make_unique<ui::CrtcController>( drm_.get(), kCrtcIdBase + 1, kConnectorIdBase + 1)); - ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr); - - EXPECT_TRUE(controller->Modeset(plane1, kDefaultMode)); + EXPECT_TRUE(ModesetController(controller)); gfx::RectF crop_rect = gfx::RectF(0, 0, 0.5, 0.5); overlay_params_.back().buffer_size = overlay_rect_.size(); @@ -505,9 +521,7 @@ TEST_F(DrmOverlayValidatorTest, ui::HardwareDisplayController* controller = window_->GetController(); controller->AddCrtc(std::make_unique<ui::CrtcController>( drm_.get(), kCrtcIdBase + 1, kConnectorIdBase + 1)); - ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr); - - EXPECT_TRUE(controller->Modeset(plane1, kDefaultMode)); + EXPECT_TRUE(ModesetController(controller)); gfx::RectF crop_rect = gfx::RectF(0, 0, 0.5, 0.5); overlay_params_.back().buffer_size = overlay_rect_.size(); @@ -547,8 +561,7 @@ TEST_F(DrmOverlayValidatorTest, OptimalFormatXRGB_MirroredControllers) { ui::HardwareDisplayController* controller = window_->GetController(); controller->AddCrtc(std::make_unique<ui::CrtcController>( drm_.get(), kCrtcIdBase + 1, kConnectorIdBase + 1)); - ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr); - EXPECT_TRUE(controller->Modeset(plane1, kDefaultMode)); + EXPECT_TRUE(ModesetController(controller)); overlay_params_.back().buffer_size = overlay_rect_.size(); overlay_params_.back().display_rect = gfx::RectF(overlay_rect_); @@ -583,8 +596,7 @@ TEST_F(DrmOverlayValidatorTest, ui::HardwareDisplayController* controller = window_->GetController(); controller->AddCrtc(std::make_unique<ui::CrtcController>( drm_.get(), kCrtcIdBase + 1, kConnectorIdBase + 1)); - ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr); - EXPECT_TRUE(controller->Modeset(plane1, kDefaultMode)); + EXPECT_TRUE(ModesetController(controller)); overlay_params_.back().buffer_size = overlay_rect_.size(); overlay_params_.back().display_rect = gfx::RectF(overlay_rect_); @@ -618,8 +630,7 @@ TEST_F(DrmOverlayValidatorTest, ui::HardwareDisplayController* controller = window_->GetController(); controller->AddCrtc(std::make_unique<ui::CrtcController>( drm_.get(), kCrtcIdBase + 1, kConnectorIdBase + 1)); - ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr); - EXPECT_TRUE(controller->Modeset(plane1, kDefaultMode)); + EXPECT_TRUE(ModesetController(controller)); overlay_params_.back().buffer_size = overlay_rect_.size(); overlay_params_.back().display_rect = gfx::RectF(overlay_rect_); diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_thread.cc b/chromium/ui/ozone/platform/drm/gpu/drm_thread.cc index 789edc91f68..52c31a7fa8f 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_thread.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_thread.cc @@ -330,17 +330,9 @@ void DrmThread::ConfigureNativeDisplays( base::OnceCallback<void(const base::flat_map<int64_t, bool>&)> callback) { TRACE_EVENT0("drm", "DrmThread::ConfigureNativeDisplays"); - base::flat_map<int64_t, bool> statuses; - for (const auto& config : config_requests) { - bool status = false; - if (config.mode) { - status = display_manager_->ConfigureDisplay( - config.id, *config.mode.value(), config.origin); - } else { - status = display_manager_->DisableDisplay(config.id); - } - statuses.insert(std::make_pair(config.id, status)); - } + base::flat_map<int64_t, bool> statuses = + display_manager_->ConfigureDisplays(config_requests); + std::move(callback).Run(statuses); } @@ -371,19 +363,27 @@ void DrmThread::RemoveGraphicsDevice(const base::FilePath& path) { void DrmThread::GetHDCPState( int64_t display_id, - base::OnceCallback<void(int64_t, bool, display::HDCPState)> callback) { + base::OnceCallback<void(int64_t, + bool, + display::HDCPState, + display::ContentProtectionMethod)> callback) { TRACE_EVENT0("drm", "DrmThread::GetHDCPState"); display::HDCPState state = display::HDCP_STATE_UNDESIRED; - bool success = display_manager_->GetHDCPState(display_id, &state); - std::move(callback).Run(display_id, success, state); + display::ContentProtectionMethod protection_method = + display::CONTENT_PROTECTION_METHOD_NONE; + bool success = + display_manager_->GetHDCPState(display_id, &state, &protection_method); + std::move(callback).Run(display_id, success, state, protection_method); } void DrmThread::SetHDCPState(int64_t display_id, display::HDCPState state, + display::ContentProtectionMethod protection_method, base::OnceCallback<void(int64_t, bool)> callback) { TRACE_EVENT0("drm", "DrmThread::SetHDCPState"); - std::move(callback).Run(display_id, - display_manager_->SetHDCPState(display_id, state)); + std::move(callback).Run( + display_id, + display_manager_->SetHDCPState(display_id, state, protection_method)); } void DrmThread::SetColorMatrix(int64_t display_id, diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_thread.h b/chromium/ui/ozone/platform/drm/gpu/drm_thread.h index a744f482bd1..a58d41063e5 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_thread.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_thread.h @@ -151,10 +151,14 @@ class DrmThread : public base::Thread, const std::vector<display::DisplayConfigurationParams>& config_requests, ConfigureNativeDisplaysCallback callback) override; void GetHDCPState(int64_t display_id, - base::OnceCallback<void(int64_t, bool, display::HDCPState)> + base::OnceCallback<void(int64_t, + bool, + display::HDCPState, + display::ContentProtectionMethod)> callback) override; void SetHDCPState(int64_t display_id, display::HDCPState state, + display::ContentProtectionMethod protection_method, base::OnceCallback<void(int64_t, bool)> callback) override; void SetColorMatrix(int64_t display_id, const std::vector<float>& color_matrix) override; diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_window_unittest.cc b/chromium/ui/ozone/platform/drm/gpu/drm_window_unittest.cc index ffb8ffb9359..1edd0cb27c0 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_window_unittest.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_window_unittest.cc @@ -6,6 +6,7 @@ #include <drm_fourcc.h> #include <stdint.h> +#include <xf86drm.h> #include <memory> #include <utility> #include <vector> @@ -84,6 +85,8 @@ class DrmWindowTest : public testing::Test { } protected: + void InitializeDrmState(ui::MockDrmDevice* drm, bool is_atomic = true); + base::test::SingleThreadTaskEnvironment task_environment_{ base::test::SingleThreadTaskEnvironment::MainThreadType::UI}; scoped_refptr<ui::MockDrmDevice> drm_; @@ -95,6 +98,14 @@ class DrmWindowTest : public testing::Test { gfx::PresentationFeedback last_presentation_feedback_; private: + struct PlaneState { + std::vector<uint32_t> formats; + }; + + struct CrtcState { + std::vector<PlaneState> planes; + }; + DISALLOW_COPY_AND_ASSIGN(DrmWindowTest); }; @@ -105,9 +116,15 @@ void DrmWindowTest::SetUp() { auto gbm_device = std::make_unique<ui::MockGbmDevice>(); drm_ = new ui::MockDrmDevice(std::move(gbm_device)); screen_manager_ = std::make_unique<ui::ScreenManager>(); + + InitializeDrmState(drm_.get()); + screen_manager_->AddDisplayController(drm_, kDefaultCrtc, kDefaultConnector); - screen_manager_->ConfigureDisplayController( - drm_, kDefaultCrtc, kDefaultConnector, gfx::Point(), kDefaultMode); + std::vector<ui::ScreenManager::ControllerConfigParams> controllers_to_enable; + controllers_to_enable.emplace_back( + 1 /*display_id*/, drm_, kDefaultCrtc, kDefaultConnector, gfx::Point(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); drm_device_manager_ = std::make_unique<ui::DrmDeviceManager>(nullptr); @@ -125,6 +142,110 @@ void DrmWindowTest::TearDown() { window->Shutdown(); } +void DrmWindowTest::InitializeDrmState(ui::MockDrmDevice* drm, bool is_atomic) { + // A Sample of CRTC states. + std::vector<CrtcState> crtc_states = { + { + /* .planes = */ + {{/* .formats = */ {DRM_FORMAT_XRGB8888}}}, + }, + { + /* .planes = */ + {{/* .formats = */ {DRM_FORMAT_XRGB8888}}}, + }, + }; + + constexpr uint32_t kPlaneIdBase = 300; + constexpr uint32_t kInFormatsBlobPropIdBase = 400; + + std::vector<ui::MockDrmDevice::CrtcProperties> crtc_properties( + crtc_states.size()); + std::map<uint32_t, std::string> crtc_property_names = { + {1000, "ACTIVE"}, + {1001, "MODE_ID"}, + }; + + std::vector<ui::MockDrmDevice::ConnectorProperties> connector_properties(3); + std::map<uint32_t, std::string> connector_property_names = { + {2000, "CRTC_ID"}, + }; + for (size_t i = 0; i < connector_properties.size(); ++i) { + connector_properties[i].id = kDefaultConnector + i; + for (const auto& pair : connector_property_names) { + connector_properties[i].properties.push_back( + {/* .id = */ pair.first, /* .value = */ 0}); + } + } + + std::vector<ui::MockDrmDevice::PlaneProperties> plane_properties; + std::map<uint32_t, std::string> plane_property_names = { + // Add all required properties. + {3000, "CRTC_ID"}, + {3001, "CRTC_X"}, + {3002, "CRTC_Y"}, + {3003, "CRTC_W"}, + {3004, "CRTC_H"}, + {3005, "FB_ID"}, + {3006, "SRC_X"}, + {3007, "SRC_Y"}, + {3008, "SRC_W"}, + {3009, "SRC_H"}, + // Defines some optional properties we use for convenience. + {3010, "type"}, + {3011, "IN_FORMATS"}, + }; + + uint32_t plane_id = kPlaneIdBase; + uint32_t property_id = kInFormatsBlobPropIdBase; + + for (size_t crtc_idx = 0; crtc_idx < crtc_states.size(); ++crtc_idx) { + crtc_properties[crtc_idx].id = kDefaultCrtc + crtc_idx; + for (const auto& pair : crtc_property_names) { + crtc_properties[crtc_idx].properties.push_back( + {/* .id = */ pair.first, /* .value = */ 0}); + } + + std::vector<ui::MockDrmDevice::PlaneProperties> crtc_plane_properties( + crtc_states[crtc_idx].planes.size()); + for (size_t plane_idx = 0; plane_idx < crtc_states[crtc_idx].planes.size(); + ++plane_idx) { + crtc_plane_properties[plane_idx].id = plane_id++; + crtc_plane_properties[plane_idx].crtc_mask = 1 << crtc_idx; + + for (const auto& pair : plane_property_names) { + uint64_t value = 0; + if (pair.first == 3010) { + value = + plane_idx == 0 ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY; + } else if (pair.first == 3011) { + value = property_id++; + drm->SetPropertyBlob(ui::MockDrmDevice::AllocateInFormatsBlob( + value, crtc_states[crtc_idx].planes[plane_idx].formats, + std::vector<drm_format_modifier>())); + } else if (pair.first >= 3001 && pair.first <= 3009) { + value = 27; + } + + crtc_plane_properties[plane_idx].properties.push_back( + {/* .id = */ pair.first, /* .value = */ value}); + } + } + + plane_properties.insert(plane_properties.end(), + crtc_plane_properties.begin(), + crtc_plane_properties.end()); + } + + std::map<uint32_t, std::string> property_names; + property_names.insert(crtc_property_names.begin(), crtc_property_names.end()); + property_names.insert(connector_property_names.begin(), + connector_property_names.end()); + property_names.insert(plane_property_names.begin(), + plane_property_names.end()); + drm->InitializeState(crtc_properties, connector_properties, plane_properties, + property_names, /*is_atomic=*/false); +} + TEST_F(DrmWindowTest, SetCursorImage) { const gfx::Size cursor_size(6, 4); screen_manager_->GetWindow(kDefaultWidgetHandle) @@ -161,10 +282,16 @@ TEST_F(DrmWindowTest, CheckCursorSurfaceAfterChangingDevice) { auto gbm_device = std::make_unique<ui::MockGbmDevice>(); scoped_refptr<ui::MockDrmDevice> drm = new ui::MockDrmDevice(std::move(gbm_device)); + InitializeDrmState(drm.get()); + screen_manager_->AddDisplayController(drm, kDefaultCrtc, kDefaultConnector); - screen_manager_->ConfigureDisplayController( - drm, kDefaultCrtc, kDefaultConnector, - gfx::Point(0, kDefaultMode.vdisplay), kDefaultMode); + + std::vector<ui::ScreenManager::ControllerConfigParams> controllers_to_enable; + controllers_to_enable.emplace_back( + 2 /*display_id*/, drm, kDefaultCrtc, kDefaultConnector, + gfx::Point(0, kDefaultMode.vdisplay), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); // Move window to the display on the new device. screen_manager_->GetWindow(kDefaultWidgetHandle) diff --git a/chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc b/chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc index 9a220c3ecdf..163a89f699c 100644 --- a/chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc +++ b/chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc @@ -24,6 +24,7 @@ #include "ui/ozone/common/egl_util.h" #include "ui/ozone/common/gl_ozone_egl.h" #include "ui/ozone/platform/drm/common/drm_util.h" +#include "ui/ozone/platform/drm/common/scoped_drm_types.h" #include "ui/ozone/platform/drm/gpu/drm_gpu_util.h" #include "ui/ozone/platform/drm/gpu/drm_thread_proxy.h" #include "ui/ozone/platform/drm/gpu/drm_window_proxy.h" @@ -185,18 +186,18 @@ std::vector<gfx::BufferFormat> EnumerateSupportedBufferFormatsForTexturing() { if (!dev_path_file.IsValid()) break; + // Skip the virtual graphics memory manager device. + ScopedDrmVersionPtr version(drmGetVersion(dev_path_file.GetPlatformFile())); + if (!version || base::LowerCaseEqualsASCII(version->name, "vgem")) { + continue; + } + ScopedGbmDevice device(gbm_create_device(dev_path_file.GetPlatformFile())); if (!device) { LOG(ERROR) << "Couldn't create Gbm Device at " << dev_path.MaybeAsASCII(); return supported_buffer_formats; } - // Skip the virtual graphics memory manager device. - if (base::LowerCaseEqualsASCII(gbm_device_get_backend_name(device.get()), - "vgem")) { - continue; - } - for (int i = 0; i <= static_cast<int>(gfx::BufferFormat::LAST); ++i) { const gfx::BufferFormat buffer_format = static_cast<gfx::BufferFormat>(i); if (base::Contains(supported_buffer_formats, buffer_format)) diff --git a/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc b/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc index a868bb5175b..90d7a20d0cd 100644 --- a/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc +++ b/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc @@ -244,7 +244,7 @@ void GbmSurfaceless::SubmitFrame() { for (auto& overlay : unsubmitted_frames_.front()->overlays) { if (overlay.z_order() == 0 && overlay.gpu_fence()) { submitted_frame_gpu_fence_ = std::make_unique<gfx::GpuFence>( - gfx::CloneHandleForIPC(overlay.gpu_fence()->GetGpuFenceHandle())); + overlay.gpu_fence()->GetGpuFenceHandle().Clone()); break; } } diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.cc b/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.cc index 7cc196aa293..b4736423e74 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.cc +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.cc @@ -73,49 +73,70 @@ HardwareDisplayController::HardwareDisplayController( HardwareDisplayController::~HardwareDisplayController() = default; -bool HardwareDisplayController::Modeset(const DrmOverlayPlane& primary, - const drmModeModeInfo& mode) { - TRACE_EVENT0("drm", "HDC::Modeset"); - return ModesetCrtc(primary, /*use_current_crtc_mode=*/false, mode); +void HardwareDisplayController::GetModesetProps(CommitRequest* commit_request, + const DrmOverlayPlane& primary, + const drmModeModeInfo& mode) { + TRACE_EVENT0("drm", "HDC::GetModesetProps"); + GetModesetPropsForCrtcs(commit_request, primary, + /*use_current_crtc_mode=*/false, mode); } -bool HardwareDisplayController::Enable(const DrmOverlayPlane& primary) { - TRACE_EVENT0("drm", "HDC::Enable"); +void HardwareDisplayController::GetEnableProps(CommitRequest* commit_request, + const DrmOverlayPlane& primary) { + TRACE_EVENT0("drm", "HDC::GetEnableProps"); + // TODO(markyacoub): Simplify and remove the use of empty_mode. drmModeModeInfo empty_mode = {}; - return ModesetCrtc(primary, /*use_current_crtc_mode=*/true, empty_mode); + GetModesetPropsForCrtcs(commit_request, primary, + /*use_current_crtc_mode=*/true, empty_mode); } -bool HardwareDisplayController::ModesetCrtc(const DrmOverlayPlane& primary, - bool use_current_crtc_mode, - const drmModeModeInfo& mode) { - DCHECK(primary.buffer.get()); - bool status = true; - for (const auto& controller : crtc_controllers_) - status &= controller->Modeset( - primary, use_current_crtc_mode ? controller->mode() : mode, - owned_hardware_planes_); +void HardwareDisplayController::GetModesetPropsForCrtcs( + CommitRequest* commit_request, + const DrmOverlayPlane& primary, + bool use_current_crtc_mode, + const drmModeModeInfo& mode) { + DCHECK(commit_request); - is_disabled_ = false; - ResetCursor(); - OnModesetComplete(primary); - return status; -} + GetDrmDevice()->plane_manager()->BeginFrame(&owned_hardware_planes_); -void HardwareDisplayController::Disable() { - TRACE_EVENT0("drm", "HDC::Disable"); + for (const auto& controller : crtc_controllers_) { + drmModeModeInfo modeset_mode = + use_current_crtc_mode ? controller->mode() : mode; - for (const auto& controller : crtc_controllers_) - // TODO(crbug.com/1015104): Modeset and Disable operations should go - // together. The current split is due to how the legacy/atomic split - // evolved. It should be cleaned up under the more generic - // HardwareDisplayPlaneManager{Legacy,Atomic} calls. - controller->Disable(); + DrmOverlayPlaneList overlays; + overlays.push_back(primary.Clone()); + + CrtcCommitRequest request = CrtcCommitRequest::EnableCrtcRequest( + controller->crtc(), controller->connector(), modeset_mode, + &owned_hardware_planes_, std::move(overlays)); + commit_request->push_back(std::move(request)); + } +} - bool ret = GetDrmDevice()->plane_manager()->DisableOverlayPlanes( - &owned_hardware_planes_); - LOG_IF(ERROR, !ret) << "Can't disable overlays when disabling HDC."; +void HardwareDisplayController::GetDisableProps(CommitRequest* commit_request) { + TRACE_EVENT0("drm", "HDC::GetDisableProps"); - is_disabled_ = true; + for (const auto& controller : crtc_controllers_) { + CrtcCommitRequest request = CrtcCommitRequest::DisableCrtcRequest( + controller->crtc(), controller->connector(), &owned_hardware_planes_); + commit_request->push_back(std::move(request)); + } +} + +void HardwareDisplayController::UpdateState( + bool is_enabled, + const DrmOverlayPlane* primary_plane) { + // TODO(markyacoub): Update how we report the controller state. Right now, the + // controller state is independent of its CRTCs; however, it should reflect + // its CRTCs active states. + is_disabled_ = !is_enabled; + + if (is_enabled) { + DCHECK(primary_plane); + // TODO(markyacoub): This should be absorbed in the commit request. + ResetCursor(); + OnModesetComplete(*primary_plane); + } } void HardwareDisplayController::SchedulePageFlip( @@ -176,12 +197,13 @@ bool HardwareDisplayController::ScheduleOrTestPageFlip( bool status = true; for (const auto& controller : crtc_controllers_) { - status &= controller->AssignOverlayPlanes(&owned_hardware_planes_, - pending_planes); + status &= controller->AssignOverlayPlanes( + &owned_hardware_planes_, pending_planes, /*is_modesetting=*/false); } status &= GetDrmDevice()->plane_manager()->Commit( - &owned_hardware_planes_, page_flip_request, out_fence); + &owned_hardware_planes_, /*should_modeset=*/false, page_flip_request, + out_fence); return status; } @@ -345,8 +367,10 @@ void HardwareDisplayController::OnPageFlipComplete( return; // Modeset occured during this page flip. time_of_last_flip_ = presentation_feedback.timestamp; current_planes_ = std::move(pending_planes); - for (const auto& controller : crtc_controllers_) - controller->OnPageFlipComplete(); + for (const auto& controller : crtc_controllers_) { + GetDrmDevice()->plane_manager()->ResetModesetBufferOfCrtc( + controller->crtc()); + } page_flip_request_ = nullptr; } @@ -357,6 +381,7 @@ void HardwareDisplayController::OnModesetComplete( // pending planes to the same values so that the callback keeps the correct // state. page_flip_request_ = nullptr; + owned_hardware_planes_.legacy_page_flips.clear(); current_planes_.clear(); current_planes_.push_back(primary.Clone()); time_of_last_flip_ = base::TimeTicks::Now(); diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.h b/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.h index 6fb71b24860..1fbd466dbfc 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.h +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.h @@ -93,15 +93,18 @@ class HardwareDisplayController { const gfx::Point& origin); ~HardwareDisplayController(); - // Performs the initial CRTC configuration. If successful, it will display the - // framebuffer for |primary| with |mode|. - bool Modeset(const DrmOverlayPlane& primary, const drmModeModeInfo& mode); - - // Performs a CRTC configuration re-using the modes from the CRTCs. - bool Enable(const DrmOverlayPlane& primary); - - // Disables the CRTC. - void Disable(); + // Gets the props required to modeset a CRTC with a |mode| onto + // |commit_request|. + void GetModesetProps(CommitRequest* commit_request, + const DrmOverlayPlane& primary, + const drmModeModeInfo& mode); + // Gets the props required to enable/disable a CRTC onto |commit_request|. + void GetEnableProps(CommitRequest* commit_request, + const DrmOverlayPlane& primary); + void GetDisableProps(CommitRequest* commit_request); + + // Updates state of the controller after modeset/enable/disable is performed. + void UpdateState(bool is_enabled, const DrmOverlayPlane* primary_plane); // Schedules the |overlays|' framebuffers to be displayed on the next vsync // event. The event will be posted on the graphics card file descriptor |fd_| @@ -169,12 +172,12 @@ class HardwareDisplayController { const gfx::PresentationFeedback& presentation_feedback); private: - // If multiple CRTC Controllers exist and they're enabled, each will be - // enabled with its own mode. Set |use_current_crtc_mode| to Modeset using - // controller's mode instead of |mode|. - bool ModesetCrtc(const DrmOverlayPlane& primary, - bool use_current_crtc_mode, - const drmModeModeInfo& mode); + // Loops over |crtc_controllers_| and save their props into |commit_request| + // to be enabled/modeset. + void GetModesetPropsForCrtcs(CommitRequest* commit_request, + const DrmOverlayPlane& primary, + bool use_current_crtc_mode, + const drmModeModeInfo& mode); void OnModesetComplete(const DrmOverlayPlane& primary); bool ScheduleOrTestPageFlip(const DrmOverlayPlaneList& plane_list, scoped_refptr<PageFlipRequest> page_flip_request, diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller_unittest.cc b/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller_unittest.cc index 3f751403fd0..3a69804048c 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller_unittest.cc +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller_unittest.cc @@ -9,6 +9,7 @@ #include <utility> #include "base/bind.h" +#include "base/files/file_util.h" #include "base/macros.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkCanvas.h" @@ -30,11 +31,19 @@ namespace { const drmModeModeInfo kDefaultMode = {0, 6, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, {'\0'}}; -constexpr uint32_t kCrtcIdBase = 1; +constexpr uint32_t kCrtcIdBase = 100; constexpr uint32_t kPrimaryCrtc = kCrtcIdBase; constexpr uint32_t kSecondaryCrtc = kCrtcIdBase + 1; -constexpr uint32_t kConnectorIdBase = 10; -constexpr uint32_t kPlaneOffset = 1000; +constexpr uint32_t kConnectorIdBase = 200; +constexpr uint32_t kPlaneOffset = 300; +constexpr uint32_t kInFormatsBlobPropId = 400; + +constexpr uint32_t kActivePropId = 1000; +constexpr uint32_t kModePropId = 1001; +constexpr uint32_t kCrtcIdPropId = 2000; +constexpr uint32_t kFenceFdPropId = 3010; +constexpr uint32_t kTypePropId = 3011; +constexpr uint32_t kInFormatsPropId = 3012; const gfx::Size kDefaultModeSize(kDefaultMode.hdisplay, kDefaultMode.vdisplay); const gfx::Size kOverlaySize(kDefaultMode.hdisplay / 2, @@ -43,6 +52,35 @@ const gfx::SizeF kDefaultModeSizeF(1.0, 1.0); } // namespace +class FakeFenceFD { + public: + FakeFenceFD(); + + std::unique_ptr<gfx::GpuFence> GetGpuFence() const; + void Signal() const; + + private: + base::ScopedFD read_fd; + base::ScopedFD write_fd; +}; + +FakeFenceFD::FakeFenceFD() { + int fds[2]; + base::CreateLocalNonBlockingPipe(fds); + read_fd = base::ScopedFD(fds[0]); + write_fd = base::ScopedFD(fds[1]); +} + +std::unique_ptr<gfx::GpuFence> FakeFenceFD::GetGpuFence() const { + gfx::GpuFenceHandle handle; + handle.owned_fd = base::ScopedFD(HANDLE_EINTR(dup(read_fd.get()))); + return std::make_unique<gfx::GpuFence>(std::move(handle)); +} + +void FakeFenceFD::Signal() const { + base::WriteFileDescriptor(write_fd.get(), "a", 1); +} + class HardwareDisplayControllerTest : public testing::Test { public: HardwareDisplayControllerTest() : page_flips_(0) {} @@ -73,6 +111,9 @@ class HardwareDisplayControllerTest : public testing::Test { } protected: + bool ModesetWithPlane(const ui::DrmOverlayPlane& plane); + bool DisableController(); + std::unique_ptr<ui::HardwareDisplayController> controller_; scoped_refptr<ui::MockDrmDevice> drm_; @@ -91,11 +132,6 @@ void HardwareDisplayControllerTest::SetUp() { auto gbm_device = std::make_unique<ui::MockGbmDevice>(); drm_ = new ui::MockDrmDevice(std::move(gbm_device)); InitializeDrmDevice(/* use_atomic= */ true); - - controller_ = std::make_unique<ui::HardwareDisplayController>( - std::make_unique<ui::CrtcController>(drm_.get(), kPrimaryCrtc, - kConnectorIdBase), - gfx::Point()); } void HardwareDisplayControllerTest::TearDown() { @@ -104,10 +140,6 @@ void HardwareDisplayControllerTest::TearDown() { } void HardwareDisplayControllerTest::InitializeDrmDevice(bool use_atomic) { - constexpr uint32_t kTypePropId = 3010; - constexpr uint32_t kInFormatsPropId = 3011; - constexpr uint32_t kInFormatsBlobPropId = 400; - std::vector<ui::MockDrmDevice::CrtcProperties> crtc_properties(2); std::map<uint32_t, std::string> crtc_property_names = { {1000, "ACTIVE"}, @@ -116,7 +148,7 @@ void HardwareDisplayControllerTest::InitializeDrmDevice(bool use_atomic) { std::vector<ui::MockDrmDevice::ConnectorProperties> connector_properties(2); std::map<uint32_t, std::string> connector_property_names = { - {2000, "CRTC_ID"}, + {kCrtcIdPropId, "CRTC_ID"}, }; for (size_t i = 0; i < connector_properties.size(); ++i) { connector_properties[i].id = kConnectorIdBase + i; @@ -139,6 +171,7 @@ void HardwareDisplayControllerTest::InitializeDrmDevice(bool use_atomic) { {3007, "SRC_Y"}, {3008, "SRC_W"}, {3009, "SRC_H"}, + {kFenceFdPropId, "IN_FENCE_FD"}, // Add some optional properties we use for convenience. {kTypePropId, "type"}, {kInFormatsPropId, "IN_FORMATS"}, @@ -181,8 +214,45 @@ void HardwareDisplayControllerTest::InitializeDrmDevice(bool use_atomic) { connector_property_names.end()); property_names.insert(plane_property_names.begin(), plane_property_names.end()); + + // This will change the plane_manager of the drm. + // HardwareDisplayController is tied to the plane_manager CRTC states. + // Destruct the controller before destructing the plane manager its CRTC + // controllers are tied to. + controller_ = nullptr; drm_->InitializeState(crtc_properties, connector_properties, plane_properties, property_names, use_atomic); + // Initialize a new HardwareDisplayController with the new Plane Manager of + // the DRM. + controller_ = std::make_unique<ui::HardwareDisplayController>( + std::make_unique<ui::CrtcController>(drm_.get(), kPrimaryCrtc, + kConnectorIdBase), + gfx::Point()); +} + +bool HardwareDisplayControllerTest::ModesetWithPlane( + const ui::DrmOverlayPlane& plane) { + ui::CommitRequest commit_request; + controller_->GetModesetProps(&commit_request, plane, kDefaultMode); + ui::CommitRequest request_for_update = commit_request; + bool status = drm_->plane_manager()->Commit(std::move(commit_request), + DRM_MODE_ATOMIC_ALLOW_MODESET); + controller_->UpdateState( + /*enabled=*/true, + ui::DrmOverlayPlane::GetPrimaryPlane(request_for_update[0].overlays())); + + return status; +} + +bool HardwareDisplayControllerTest::DisableController() { + ui::CommitRequest commit_request; + controller_->GetDisableProps(&commit_request); + ui::CommitRequest request_for_update = commit_request; + bool status = drm_->plane_manager()->Commit(std::move(commit_request), + DRM_MODE_ATOMIC_ALLOW_MODESET); + controller_->UpdateState(/*enabled=*/false, nullptr); + + return status; } void HardwareDisplayControllerTest::SchedulePageFlip( @@ -221,14 +291,148 @@ uint64_t HardwareDisplayControllerTest::GetPlanePropertyValue( TEST_F(HardwareDisplayControllerTest, CheckModesettingResult) { ui::DrmOverlayPlane plane(CreateBuffer(), nullptr); - EXPECT_TRUE(controller_->Modeset(plane, kDefaultMode)); + EXPECT_TRUE(ModesetWithPlane(plane)); EXPECT_FALSE(plane.buffer->HasOneRef()); } +TEST_F(HardwareDisplayControllerTest, CheckModesettingSetsProps) { + ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr); + + EXPECT_TRUE(ModesetWithPlane(plane1)); + + ui::DrmOverlayPlane plane2(CreateBuffer(), nullptr); + std::vector<ui::DrmOverlayPlane> planes = {}; + planes.push_back(plane2.Clone()); + + SchedulePageFlip(std::move(planes)); + + // Test props values after modesetting. + ui::DrmDevice::Property connector_prop_crtc_id = {}; + ui::ScopedDrmObjectPropertyPtr connector_props = + drm_->GetObjectProperties(kConnectorIdBase, DRM_MODE_OBJECT_CONNECTOR); + ui::GetDrmPropertyForName(drm_.get(), connector_props.get(), "CRTC_ID", + &connector_prop_crtc_id); + EXPECT_EQ(kCrtcIdPropId, connector_prop_crtc_id.id); + EXPECT_EQ(kCrtcIdBase, connector_prop_crtc_id.value); + + ui::DrmDevice::Property crtc_prop_for_name = {}; + ui::ScopedDrmObjectPropertyPtr crtc_props = + drm_->GetObjectProperties(kPrimaryCrtc, DRM_MODE_OBJECT_CRTC); + GetDrmPropertyForName(drm_.get(), crtc_props.get(), "ACTIVE", + &crtc_prop_for_name); + EXPECT_EQ(kActivePropId, crtc_prop_for_name.id); + EXPECT_EQ(1U, crtc_prop_for_name.value); + + GetDrmPropertyForName(drm_.get(), crtc_props.get(), "MODE_ID", + &crtc_prop_for_name); + EXPECT_EQ(kModePropId, crtc_prop_for_name.id); + + ui::DrmDevice::Property fence_fd_prop = {}; + ui::ScopedDrmObjectPropertyPtr plane_props = + drm_->GetObjectProperties(kPlaneOffset, DRM_MODE_OBJECT_PLANE); + ui::GetDrmPropertyForName(drm_.get(), plane_props.get(), "IN_FENCE_FD", + &fence_fd_prop); + EXPECT_EQ(kFenceFdPropId, fence_fd_prop.id); + EXPECT_EQ(base::kInvalidPlatformFile, static_cast<int>(fence_fd_prop.value)); +} + +TEST_F(HardwareDisplayControllerTest, FenceFdValueChange) { + ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr); + EXPECT_TRUE(ModesetWithPlane(plane1)); + + // Test invalid fence fd + { + ui::DrmDevice::Property fence_fd_prop = {}; + ui::ScopedDrmObjectPropertyPtr plane_props = + drm_->GetObjectProperties(kPlaneOffset, DRM_MODE_OBJECT_PLANE); + ui::GetDrmPropertyForName(drm_.get(), plane_props.get(), "IN_FENCE_FD", + &fence_fd_prop); + EXPECT_EQ(kFenceFdPropId, fence_fd_prop.id); + EXPECT_EQ(base::kInvalidPlatformFile, + static_cast<int>(fence_fd_prop.value)); + } + + const FakeFenceFD fake_fence_fd; + plane1.gpu_fence = fake_fence_fd.GetGpuFence(); + std::vector<ui::DrmOverlayPlane> planes = {}; + planes.push_back(plane1.Clone()); + SchedulePageFlip(std::move(planes)); + + // Verify fence FD after a GPU Fence is added to the plane. + { + ui::DrmDevice::Property fence_fd_prop = {}; + ui::ScopedDrmObjectPropertyPtr plane_props = + drm_->GetObjectProperties(kPlaneOffset, DRM_MODE_OBJECT_PLANE); + ui::GetDrmPropertyForName(drm_.get(), plane_props.get(), "IN_FENCE_FD", + &fence_fd_prop); + EXPECT_EQ(kFenceFdPropId, fence_fd_prop.id); + EXPECT_LT(base::kInvalidPlatformFile, + static_cast<int>(fence_fd_prop.value)); + } + + plane1.gpu_fence = nullptr; + EXPECT_TRUE(ModesetWithPlane(plane1)); + + // Test an invalid FD again after the fence is removed. + { + ui::DrmDevice::Property fence_fd_prop = {}; + ui::ScopedDrmObjectPropertyPtr plane_props = + drm_->GetObjectProperties(kPlaneOffset, DRM_MODE_OBJECT_PLANE); + ui::GetDrmPropertyForName(drm_.get(), plane_props.get(), "IN_FENCE_FD", + &fence_fd_prop); + EXPECT_EQ(kFenceFdPropId, fence_fd_prop.id); + EXPECT_EQ(base::kInvalidPlatformFile, + static_cast<int>(fence_fd_prop.value)); + } +} + +TEST_F(HardwareDisplayControllerTest, CheckDisableResetsProps) { + ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr); + + EXPECT_TRUE(ModesetWithPlane(plane1)); + + ui::DrmOverlayPlane plane2(CreateBuffer(), nullptr); + std::vector<ui::DrmOverlayPlane> planes = {}; + planes.push_back(plane2.Clone()); + + SchedulePageFlip(std::move(planes)); + + // Test props values after disabling. + DisableController(); + + ui::DrmDevice::Property connector_prop_crtc_id; + ui::ScopedDrmObjectPropertyPtr connector_props = + drm_->GetObjectProperties(kConnectorIdBase, DRM_MODE_OBJECT_CONNECTOR); + ui::GetDrmPropertyForName(drm_.get(), connector_props.get(), "CRTC_ID", + &connector_prop_crtc_id); + EXPECT_EQ(0U, connector_prop_crtc_id.value); + + ui::DrmDevice::Property crtc_prop_for_name; + ui::ScopedDrmObjectPropertyPtr crtc_props = + drm_->GetObjectProperties(kPrimaryCrtc, DRM_MODE_OBJECT_CRTC); + GetDrmPropertyForName(drm_.get(), crtc_props.get(), "ACTIVE", + &crtc_prop_for_name); + EXPECT_EQ(0U, crtc_prop_for_name.value); + + crtc_props = drm_->GetObjectProperties(kPrimaryCrtc, DRM_MODE_OBJECT_CRTC); + GetDrmPropertyForName(drm_.get(), crtc_props.get(), "MODE_ID", + &crtc_prop_for_name); + EXPECT_EQ(0U, crtc_prop_for_name.value); + + ui::DrmDevice::Property fence_fd_prop = {}; + ui::ScopedDrmObjectPropertyPtr plane_props = + drm_->GetObjectProperties(kPlaneOffset, DRM_MODE_OBJECT_PLANE); + ui::GetDrmPropertyForName(drm_.get(), plane_props.get(), "IN_FENCE_FD", + &fence_fd_prop); + EXPECT_EQ(kFenceFdPropId, fence_fd_prop.id); + EXPECT_EQ(base::kInvalidPlatformFile, static_cast<int>(fence_fd_prop.value)); +} + TEST_F(HardwareDisplayControllerTest, CheckStateAfterPageFlip) { ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr); - EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode)); + EXPECT_TRUE(ModesetWithPlane(plane1)); + EXPECT_EQ(1, drm_->get_commit_count()); ui::DrmOverlayPlane plane2(CreateBuffer(), nullptr); std::vector<ui::DrmOverlayPlane> planes; @@ -242,32 +446,19 @@ TEST_F(HardwareDisplayControllerTest, CheckStateAfterPageFlip) { EXPECT_EQ(gfx::SwapResult::SWAP_ACK, last_swap_result_); EXPECT_EQ(1, page_flips_); - EXPECT_EQ(1, drm_->get_commit_count()); + EXPECT_EQ(2, drm_->get_commit_count()); // Verify only the primary display have a valid framebuffer. EXPECT_NE(0u, GetPlanePropertyValue(kPlaneOffset, "FB_ID")); EXPECT_EQ(0u, GetPlanePropertyValue(kPlaneOffset + 1, "FB_ID")); } TEST_F(HardwareDisplayControllerTest, CheckStateIfModesetFails) { + InitializeDrmDevice(/* use_atomic */ false); drm_->set_set_crtc_expectation(false); ui::DrmOverlayPlane plane(CreateBuffer(), nullptr); - EXPECT_FALSE(controller_->Modeset(plane, kDefaultMode)); -} - -TEST_F(HardwareDisplayControllerTest, CheckStateIfPageFlipFails) { - drm_->set_commit_expectation(false); - - ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr); - - EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode)); - - ui::DrmOverlayPlane plane2(CreateBuffer(), nullptr); - std::vector<ui::DrmOverlayPlane> planes; - planes.push_back(plane2.Clone()); - EXPECT_DEATH_IF_SUPPORTED(SchedulePageFlip(std::move(planes)), - "SchedulePageFlip failed"); + EXPECT_FALSE(ModesetWithPlane(plane)); } TEST_F(HardwareDisplayControllerTest, CheckOverlayPresent) { @@ -276,7 +467,8 @@ TEST_F(HardwareDisplayControllerTest, CheckOverlayPresent) { CreateOverlayBuffer(), 1, gfx::OVERLAY_TRANSFORM_NONE, gfx::Rect(kOverlaySize), gfx::RectF(kDefaultModeSizeF), true, nullptr); - EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode)); + EXPECT_TRUE(ModesetWithPlane(plane1)); + EXPECT_EQ(1, drm_->get_commit_count()); std::vector<ui::DrmOverlayPlane> planes; planes.push_back(plane1.Clone()); @@ -286,7 +478,7 @@ TEST_F(HardwareDisplayControllerTest, CheckOverlayPresent) { drm_->RunCallbacks(); EXPECT_EQ(gfx::SwapResult::SWAP_ACK, last_swap_result_); EXPECT_EQ(1, page_flips_); - EXPECT_EQ(1, drm_->get_commit_count()); + EXPECT_EQ(2, drm_->get_commit_count()); // Verify both planes on the primary display have a valid framebuffer. EXPECT_NE(0u, GetPlanePropertyValue(kPlaneOffset, "FB_ID")); EXPECT_NE(0u, GetPlanePropertyValue(kPlaneOffset + 1, "FB_ID")); @@ -298,14 +490,15 @@ TEST_F(HardwareDisplayControllerTest, CheckOverlayTestMode) { CreateOverlayBuffer(), 1, gfx::OVERLAY_TRANSFORM_NONE, gfx::Rect(kOverlaySize), gfx::RectF(kDefaultModeSizeF), true, nullptr); - EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode)); + EXPECT_TRUE(ModesetWithPlane(plane1)); + EXPECT_EQ(1, drm_->get_commit_count()); std::vector<ui::DrmOverlayPlane> planes; planes.push_back(plane1.Clone()); planes.push_back(plane2.Clone()); SchedulePageFlip(ui::DrmOverlayPlane::Clone(planes)); - EXPECT_EQ(1, drm_->get_commit_count()); + EXPECT_EQ(2, drm_->get_commit_count()); // Verify both planes on the primary display have a valid framebuffer. EXPECT_NE(0u, GetPlanePropertyValue(kPlaneOffset, "FB_ID")); EXPECT_NE(0u, GetPlanePropertyValue(kPlaneOffset + 1, "FB_ID")); @@ -315,14 +508,14 @@ TEST_F(HardwareDisplayControllerTest, CheckOverlayTestMode) { drm_->RunCallbacks(); EXPECT_EQ(gfx::SwapResult::SWAP_ACK, last_swap_result_); EXPECT_EQ(1, page_flips_); - EXPECT_EQ(2, drm_->get_commit_count()); + EXPECT_EQ(3, drm_->get_commit_count()); // Regular flips should continue on normally. SchedulePageFlip(ui::DrmOverlayPlane::Clone(planes)); drm_->RunCallbacks(); EXPECT_EQ(gfx::SwapResult::SWAP_ACK, last_swap_result_); EXPECT_EQ(2, page_flips_); - EXPECT_EQ(3, drm_->get_commit_count()); + EXPECT_EQ(4, drm_->get_commit_count()); // Verify both planes on the primary display have a valid framebuffer. EXPECT_NE(0u, GetPlanePropertyValue(kPlaneOffset, "FB_ID")); EXPECT_NE(0u, GetPlanePropertyValue(kPlaneOffset + 1, "FB_ID")); @@ -334,7 +527,7 @@ TEST_F(HardwareDisplayControllerTest, AcceptUnderlays) { gfx::Rect(kDefaultModeSize), gfx::RectF(kDefaultModeSizeF), true, nullptr); - EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode)); + EXPECT_TRUE(ModesetWithPlane(plane1)); std::vector<ui::DrmOverlayPlane> planes; planes.push_back(plane1.Clone()); @@ -347,13 +540,12 @@ TEST_F(HardwareDisplayControllerTest, AcceptUnderlays) { } TEST_F(HardwareDisplayControllerTest, PageflipMirroredControllers) { - controller_->AddCrtc( - std::unique_ptr<ui::CrtcController>(new ui::CrtcController( - drm_.get(), kSecondaryCrtc, kConnectorIdBase + 1))); + controller_->AddCrtc(std::make_unique<ui::CrtcController>( + drm_.get(), kSecondaryCrtc, kConnectorIdBase + 1)); ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr); - EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode)); - EXPECT_EQ(2, drm_->get_set_crtc_call_count()); + EXPECT_TRUE(ModesetWithPlane(plane1)); + EXPECT_EQ(1, drm_->get_commit_count()); ui::DrmOverlayPlane plane2(CreateBuffer(), nullptr); std::vector<ui::DrmOverlayPlane> planes; @@ -362,7 +554,7 @@ TEST_F(HardwareDisplayControllerTest, PageflipMirroredControllers) { drm_->RunCallbacks(); EXPECT_EQ(gfx::SwapResult::SWAP_ACK, last_swap_result_); EXPECT_EQ(1, page_flips_); - EXPECT_EQ(1, drm_->get_commit_count()); + EXPECT_EQ(2, drm_->get_commit_count()); // Verify only the displays have a valid framebuffer on the primary plane. // First display: EXPECT_NE(0u, GetPlanePropertyValue(kPlaneOffset, "FB_ID")); @@ -373,12 +565,11 @@ TEST_F(HardwareDisplayControllerTest, PageflipMirroredControllers) { } TEST_F(HardwareDisplayControllerTest, PlaneStateAfterRemoveCrtc) { - controller_->AddCrtc( - std::unique_ptr<ui::CrtcController>(new ui::CrtcController( - drm_.get(), kSecondaryCrtc, kConnectorIdBase + 1))); + controller_->AddCrtc(std::make_unique<ui::CrtcController>( + drm_.get(), kSecondaryCrtc, kConnectorIdBase + 1)); ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr); - EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode)); + EXPECT_TRUE(ModesetWithPlane(plane1)); std::vector<ui::DrmOverlayPlane> planes; planes.push_back(plane1.Clone()); SchedulePageFlip(ui::DrmOverlayPlane::Clone(planes)); @@ -400,11 +591,10 @@ TEST_F(HardwareDisplayControllerTest, PlaneStateAfterRemoveCrtc) { EXPECT_EQ(kPrimaryCrtc, primary_crtc_plane->owning_crtc()); EXPECT_EQ(kSecondaryCrtc, secondary_crtc_plane->owning_crtc()); - // Removing the crtc should not free the plane or change ownership. + // Removing the crtc should free the plane. std::unique_ptr<ui::CrtcController> crtc = controller_->RemoveCrtc(drm_, kPrimaryCrtc); - EXPECT_TRUE(primary_crtc_plane->in_use()); - EXPECT_EQ(kPrimaryCrtc, primary_crtc_plane->owning_crtc()); + EXPECT_FALSE(primary_crtc_plane->in_use()); EXPECT_TRUE(secondary_crtc_plane->in_use()); EXPECT_EQ(kSecondaryCrtc, secondary_crtc_plane->owning_crtc()); @@ -414,15 +604,14 @@ TEST_F(HardwareDisplayControllerTest, PlaneStateAfterRemoveCrtc) { drm_->RunCallbacks(); EXPECT_EQ(gfx::SwapResult::SWAP_ACK, last_swap_result_); EXPECT_EQ(2, page_flips_); - EXPECT_TRUE(primary_crtc_plane->in_use()); - EXPECT_EQ(kPrimaryCrtc, primary_crtc_plane->owning_crtc()); + EXPECT_FALSE(primary_crtc_plane->in_use()); EXPECT_TRUE(secondary_crtc_plane->in_use()); EXPECT_EQ(kSecondaryCrtc, secondary_crtc_plane->owning_crtc()); } TEST_F(HardwareDisplayControllerTest, PlaneStateAfterDestroyingCrtc) { ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr); - EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode)); + EXPECT_TRUE(ModesetWithPlane(plane1)); std::vector<ui::DrmOverlayPlane> planes; planes.push_back(plane1.Clone()); SchedulePageFlip(std::move(planes)); @@ -446,12 +635,11 @@ TEST_F(HardwareDisplayControllerTest, PlaneStateAfterDestroyingCrtc) { } TEST_F(HardwareDisplayControllerTest, PlaneStateAfterAddCrtc) { - controller_->AddCrtc( - std::unique_ptr<ui::CrtcController>(new ui::CrtcController( - drm_.get(), kSecondaryCrtc, kConnectorIdBase + 1))); + controller_->AddCrtc(std::make_unique<ui::CrtcController>( + drm_.get(), kSecondaryCrtc, kConnectorIdBase + 1)); ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr); - EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode)); + EXPECT_TRUE(ModesetWithPlane(plane1)); std::vector<ui::DrmOverlayPlane> planes; planes.push_back(plane1.Clone()); SchedulePageFlip(ui::DrmOverlayPlane::Clone(planes)); @@ -474,8 +662,7 @@ TEST_F(HardwareDisplayControllerTest, PlaneStateAfterAddCrtc) { drm_->RunCallbacks(); EXPECT_EQ(gfx::SwapResult::SWAP_ACK, last_swap_result_); EXPECT_EQ(2, page_flips_); - EXPECT_TRUE(primary_crtc_plane->in_use()); - EXPECT_EQ(kPrimaryCrtc, primary_crtc_plane->owning_crtc()); + EXPECT_FALSE(primary_crtc_plane->in_use()); // We reset state of plane here to test that the plane was actually added to // hdc_controller. In which case, the right state should be set to plane @@ -497,33 +684,35 @@ TEST_F(HardwareDisplayControllerTest, PlaneStateAfterAddCrtc) { TEST_F(HardwareDisplayControllerTest, ModesetWhilePageFlipping) { ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr); - EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode)); + EXPECT_TRUE(ModesetWithPlane(plane1)); std::vector<ui::DrmOverlayPlane> planes; planes.push_back(plane1.Clone()); SchedulePageFlip(std::move(planes)); - EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode)); + EXPECT_TRUE(ModesetWithPlane(plane1)); drm_->RunCallbacks(); EXPECT_EQ(gfx::SwapResult::SWAP_ACK, last_swap_result_); EXPECT_EQ(1, page_flips_); } TEST_F(HardwareDisplayControllerTest, FailPageFlipping) { - drm_->set_commit_expectation(false); - ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr); - EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode)); + EXPECT_TRUE(ModesetWithPlane(plane1)); + + drm_->set_commit_expectation(false); std::vector<ui::DrmOverlayPlane> planes; planes.push_back(plane1.Clone()); EXPECT_DEATH_IF_SUPPORTED(SchedulePageFlip(std::move(planes)), "SchedulePageFlip failed"); } -TEST_F(HardwareDisplayControllerTest, CheckNoPrimaryPlane) { +TEST_F(HardwareDisplayControllerTest, CheckNoPrimaryPlaneOnFlip) { + ui::DrmOverlayPlane modeset_primary_plane(CreateBuffer(), nullptr); + EXPECT_TRUE(ModesetWithPlane(modeset_primary_plane)); + ui::DrmOverlayPlane plane1(CreateBuffer(), 1, gfx::OVERLAY_TRANSFORM_NONE, gfx::Rect(kDefaultModeSize), gfx::RectF(0, 0, 1, 1), true, nullptr); - EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode)); std::vector<ui::DrmOverlayPlane> planes; planes.push_back(plane1.Clone()); SchedulePageFlip(std::move(planes)); @@ -535,14 +724,13 @@ TEST_F(HardwareDisplayControllerTest, CheckNoPrimaryPlane) { TEST_F(HardwareDisplayControllerTest, AddCrtcMidPageFlip) { ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr); - EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode)); + EXPECT_TRUE(ModesetWithPlane(plane1)); std::vector<ui::DrmOverlayPlane> planes; planes.push_back(plane1.Clone()); SchedulePageFlip(std::move(planes)); - controller_->AddCrtc( - std::unique_ptr<ui::CrtcController>(new ui::CrtcController( - drm_.get(), kSecondaryCrtc, kConnectorIdBase + 1))); + controller_->AddCrtc(std::make_unique<ui::CrtcController>( + drm_.get(), kSecondaryCrtc, kConnectorIdBase + 1)); drm_->RunCallbacks(); EXPECT_EQ(gfx::SwapResult::SWAP_ACK, last_swap_result_); @@ -551,7 +739,7 @@ TEST_F(HardwareDisplayControllerTest, AddCrtcMidPageFlip) { TEST_F(HardwareDisplayControllerTest, RemoveCrtcMidPageFlip) { ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr); - EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode)); + EXPECT_TRUE(ModesetWithPlane(plane1)); std::vector<ui::DrmOverlayPlane> planes; planes.push_back(plane1.Clone()); SchedulePageFlip(std::move(planes)); @@ -568,7 +756,7 @@ TEST_F(HardwareDisplayControllerTest, Disable) { InitializeDrmDevice(/* use_atomic= */ true); ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr); - EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode)); + EXPECT_TRUE(ModesetWithPlane(plane1)); ui::DrmOverlayPlane plane2( CreateOverlayBuffer(), 1, gfx::OVERLAY_TRANSFORM_NONE, @@ -581,13 +769,13 @@ TEST_F(HardwareDisplayControllerTest, Disable) { drm_->RunCallbacks(); EXPECT_EQ(gfx::SwapResult::SWAP_ACK, last_swap_result_); - controller_->Disable(); + EXPECT_TRUE(DisableController()); int planes_in_use = 0; for (const auto& plane : drm_->plane_manager()->planes()) { if (plane->in_use()) planes_in_use++; } - // Only the primary plane is in use. - ASSERT_EQ(1, planes_in_use); + // No plane should be in use. + ASSERT_EQ(0, planes_in_use); } diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane.cc b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane.cc index 272a86d8166..c2a24263831 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane.cc +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane.cc @@ -12,36 +12,10 @@ #include "ui/ozone/platform/drm/gpu/drm_device.h" #include "ui/ozone/platform/drm/gpu/drm_gpu_util.h" -#ifndef DRM_PLANE_TYPE_OVERLAY -#define DRM_PLANE_TYPE_OVERLAY 0 -#endif - -#ifndef DRM_PLANE_TYPE_PRIMARY -#define DRM_PLANE_TYPE_PRIMARY 1 -#endif - -#ifndef DRM_PLANE_TYPE_CURSOR -#define DRM_PLANE_TYPE_CURSOR 2 -#endif - namespace ui { namespace { -HardwareDisplayPlane::Type GetPlaneType(int value) { - switch (value) { - case DRM_PLANE_TYPE_CURSOR: - return HardwareDisplayPlane::kCursor; - case DRM_PLANE_TYPE_PRIMARY: - return HardwareDisplayPlane::kPrimary; - case DRM_PLANE_TYPE_OVERLAY: - return HardwareDisplayPlane::kOverlay; - default: - NOTREACHED(); - return HardwareDisplayPlane::kDummy; - } -} - void ParseSupportedFormatsAndModifiers( drmModePropertyBlobPtr blob, std::vector<uint32_t>* supported_formats, @@ -91,7 +65,7 @@ bool HardwareDisplayPlane::Initialize(DrmDevice* drm) { } if (properties_.type.id) - type_ = GetPlaneType(properties_.type.value); + type_ = properties_.type.value; if (properties_.plane_color_encoding.id) { color_encoding_bt601_ = diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane.h b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane.h index ea614863ba4..aea531a7a2a 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane.h +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane.h @@ -20,8 +20,6 @@ namespace ui { class HardwareDisplayPlane { public: - enum Type { kDummy, kPrimary, kOverlay, kCursor }; - HardwareDisplayPlane(uint32_t id); virtual ~HardwareDisplayPlane(); @@ -39,8 +37,7 @@ class HardwareDisplayPlane { uint32_t id() const { return id_; } - Type type() const { return type_; } - void set_type(const Type type) { type_ = type; } + uint32_t type() const { return type_; } void set_owning_crtc(uint32_t crtc) { owning_crtc_ = crtc; } uint32_t owning_crtc() const { return owning_crtc_; } @@ -80,7 +77,7 @@ class HardwareDisplayPlane { uint32_t owning_crtc_ = 0; uint32_t last_used_format_ = 0; bool in_use_ = false; - Type type_ = kPrimary; + uint32_t type_ = DRM_PLANE_TYPE_PRIMARY; std::vector<uint32_t> supported_formats_; std::vector<drm_format_modifier> supported_format_modifiers_; diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.cc b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.cc index 7b50c3c8fea..acff6ed2116 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.cc +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.cc @@ -38,7 +38,8 @@ uint32_t OverlayTransformToDrmRotationPropertyValue( // TODO(https://crbug/880464): Remove this. bool IsRotationTransformSupported(gfx::OverlayTransform transform) { if ((transform == gfx::OVERLAY_TRANSFORM_ROTATE_90) || - (transform == gfx::OVERLAY_TRANSFORM_ROTATE_270)) { + (transform == gfx::OVERLAY_TRANSFORM_ROTATE_270) || + (transform == gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL)) { return false; } @@ -73,8 +74,7 @@ bool HardwareDisplayPlaneAtomic::Initialize(DrmDevice* drm) { return ret; } -bool HardwareDisplayPlaneAtomic::SetPlaneData( - drmModeAtomicReq* property_set, +bool HardwareDisplayPlaneAtomic::AssignPlaneProps( uint32_t crtc_id, uint32_t framebuffer, const gfx::Rect& crtc_rect, @@ -87,52 +87,65 @@ bool HardwareDisplayPlaneAtomic::SetPlaneData( if (!IsRotationTransformSupported(transform)) return false; - properties_.crtc_id.value = crtc_id; - properties_.crtc_x.value = crtc_rect.x(); - properties_.crtc_y.value = crtc_rect.y(); - properties_.crtc_w.value = crtc_rect.width(); - properties_.crtc_h.value = crtc_rect.height(); - properties_.fb_id.value = framebuffer; - properties_.src_x.value = src_rect.x(); - properties_.src_y.value = src_rect.y(); - properties_.src_w.value = src_rect.width(); - properties_.src_h.value = src_rect.height(); + // Make a copy of properties to get the props IDs for the new intermediate + // values. + assigned_props_ = properties_; + + assigned_props_.crtc_id.value = crtc_id; + assigned_props_.crtc_x.value = crtc_rect.x(); + assigned_props_.crtc_y.value = crtc_rect.y(); + assigned_props_.crtc_w.value = crtc_rect.width(); + assigned_props_.crtc_h.value = crtc_rect.height(); + assigned_props_.fb_id.value = framebuffer; + assigned_props_.src_x.value = src_rect.x(); + assigned_props_.src_y.value = src_rect.y(); + assigned_props_.src_w.value = src_rect.width(); + assigned_props_.src_h.value = src_rect.height(); + + if (assigned_props_.rotation.id) { + assigned_props_.rotation.value = + OverlayTransformToDrmRotationPropertyValue(transform); + } + + if (assigned_props_.in_fence_fd.id) + assigned_props_.in_fence_fd.value = static_cast<uint64_t>(in_fence_fd); + return true; +} + +bool HardwareDisplayPlaneAtomic::SetPlaneProps(drmModeAtomicReq* property_set) { bool plane_set_succeeded = - AddPropertyIfValid(property_set, id_, properties_.crtc_id) && - AddPropertyIfValid(property_set, id_, properties_.crtc_x) && - AddPropertyIfValid(property_set, id_, properties_.crtc_y) && - AddPropertyIfValid(property_set, id_, properties_.crtc_w) && - AddPropertyIfValid(property_set, id_, properties_.crtc_h) && - AddPropertyIfValid(property_set, id_, properties_.fb_id) && - AddPropertyIfValid(property_set, id_, properties_.src_x) && - AddPropertyIfValid(property_set, id_, properties_.src_y) && - AddPropertyIfValid(property_set, id_, properties_.src_w) && - AddPropertyIfValid(property_set, id_, properties_.src_h); - - if (properties_.rotation.id) { - properties_.rotation.value = - OverlayTransformToDrmRotationPropertyValue(transform); - plane_set_succeeded = - plane_set_succeeded && - AddPropertyIfValid(property_set, id_, properties_.rotation); + AddPropertyIfValid(property_set, id_, assigned_props_.crtc_id) && + AddPropertyIfValid(property_set, id_, assigned_props_.crtc_x) && + AddPropertyIfValid(property_set, id_, assigned_props_.crtc_y) && + AddPropertyIfValid(property_set, id_, assigned_props_.crtc_w) && + AddPropertyIfValid(property_set, id_, assigned_props_.crtc_h) && + AddPropertyIfValid(property_set, id_, assigned_props_.fb_id) && + AddPropertyIfValid(property_set, id_, assigned_props_.src_x) && + AddPropertyIfValid(property_set, id_, assigned_props_.src_y) && + AddPropertyIfValid(property_set, id_, assigned_props_.src_w) && + AddPropertyIfValid(property_set, id_, assigned_props_.src_h); + + if (assigned_props_.rotation.id) { + plane_set_succeeded &= + AddPropertyIfValid(property_set, id_, assigned_props_.rotation); } - if (properties_.in_fence_fd.id && in_fence_fd >= 0) { - properties_.in_fence_fd.value = in_fence_fd; - plane_set_succeeded = - plane_set_succeeded && - AddPropertyIfValid(property_set, id_, properties_.in_fence_fd); + if (assigned_props_.in_fence_fd.id) { + plane_set_succeeded &= + AddPropertyIfValid(property_set, id_, assigned_props_.in_fence_fd); } - if (properties_.plane_color_encoding.id) { - properties_.plane_color_encoding.value = color_encoding_bt601_; - properties_.plane_color_range.value = color_range_limited_; - plane_set_succeeded = - plane_set_succeeded && - AddPropertyIfValid(property_set, id_, - properties_.plane_color_encoding) && - AddPropertyIfValid(property_set, id_, properties_.plane_color_range); + if (assigned_props_.plane_color_encoding.id) { + // TODO(markyacoub): |color_encoding_bt601_| and |color_range_limited_| are + // only set in Initialize(). The properties could be set once in there and + // these member variables could be removed. + assigned_props_.plane_color_encoding.value = color_encoding_bt601_; + assigned_props_.plane_color_range.value = color_range_limited_; + plane_set_succeeded &= AddPropertyIfValid( + property_set, id_, assigned_props_.plane_color_encoding); + plane_set_succeeded &= AddPropertyIfValid( + property_set, id_, assigned_props_.plane_color_range); } if (!plane_set_succeeded) { @@ -140,7 +153,8 @@ bool HardwareDisplayPlaneAtomic::SetPlaneData( return false; } - crtc_id_ = crtc_id; + // Update properties_ if the setting the props succeeded. + properties_ = assigned_props_; return true; } @@ -153,4 +167,8 @@ bool HardwareDisplayPlaneAtomic::SetPlaneCtm(drmModeAtomicReq* property_set, return AddPropertyIfValid(property_set, id_, properties_.plane_ctm); } +uint32_t HardwareDisplayPlaneAtomic::AssignedCrtcId() const { + return assigned_props_.crtc_id.value; +} + } // namespace ui diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.h b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.h index af874abf6c7..5043b987468 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.h +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.h @@ -20,25 +20,28 @@ namespace ui { class HardwareDisplayPlaneAtomic : public HardwareDisplayPlane { public: - HardwareDisplayPlaneAtomic(uint32_t id); + explicit HardwareDisplayPlaneAtomic(uint32_t id); ~HardwareDisplayPlaneAtomic() override; bool Initialize(DrmDevice* drm) override; - virtual bool SetPlaneData(drmModeAtomicReq* property_set, - uint32_t crtc_id, - uint32_t framebuffer, - const gfx::Rect& crtc_rect, - const gfx::Rect& src_rect, - const gfx::OverlayTransform transform, - int in_fence_fd); + // Saves the props locally onto the plane to be committed later. + virtual bool AssignPlaneProps(uint32_t crtc_id, + uint32_t framebuffer, + const gfx::Rect& crtc_rect, + const gfx::Rect& src_rect, + const gfx::OverlayTransform transform, + int in_fence_fd); + // Sets the props on |property_set| for commit. + bool SetPlaneProps(drmModeAtomicReq* property_set); bool SetPlaneCtm(drmModeAtomicReq* property_set, uint32_t ctm_blob_id); - uint32_t crtc_id() { return crtc_id_; } + uint32_t AssignedCrtcId() const; private: - uint32_t crtc_id_ = 0; + // Intermediate variable between Assign()ment and Set()ting. + HardwareDisplayPlane::Properties assigned_props_; DISALLOW_COPY_AND_ASSIGN(HardwareDisplayPlaneAtomic); }; diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_dummy.cc b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_dummy.cc deleted file mode 100644 index d00a0a1a56a..00000000000 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_dummy.cc +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2018 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/hardware_display_plane_dummy.h" - -#include <drm_fourcc.h> - -namespace ui { - -HardwareDisplayPlaneDummy::HardwareDisplayPlaneDummy(uint32_t id, - uint32_t crtc_mask) - : HardwareDisplayPlane(id) { - crtc_mask_ = crtc_mask; -} - -HardwareDisplayPlaneDummy::~HardwareDisplayPlaneDummy() {} - -bool HardwareDisplayPlaneDummy::Initialize(DrmDevice* drm) { - type_ = kDummy; - supported_formats_.push_back(DRM_FORMAT_XRGB8888); - supported_formats_.push_back(DRM_FORMAT_XBGR8888); - return true; -} - -} // namespace ui diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_dummy.h b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_dummy.h deleted file mode 100644 index 8aadee80506..00000000000 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_dummy.h +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2018 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. - -#ifndef UI_OZONE_PLATFORM_DRM_GPU_HARDWARE_DISPLAY_PLANE_DUMMY_H_ -#define UI_OZONE_PLATFORM_DRM_GPU_HARDWARE_DISPLAY_PLANE_DUMMY_H_ - -#include "ui/ozone/platform/drm/gpu/hardware_display_plane.h" - -namespace ui { - -// Fake plane used with DRM legacy when universal planes are not supported and -// the kernel doesn't report the primary plane. -class HardwareDisplayPlaneDummy : public HardwareDisplayPlane { - public: - HardwareDisplayPlaneDummy(uint32_t id, uint32_t crtc_mask); - ~HardwareDisplayPlaneDummy() override; - - bool Initialize(DrmDevice* drm) override; - - private: - DISALLOW_COPY_AND_ASSIGN(HardwareDisplayPlaneDummy); -}; - -} // namespace ui - -#endif // UI_OZONE_PLATFORM_DRM_GPU_HARDWARE_DISPLAY_PLANE_DUMMY_H_ diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc index 0cdaaca15cf..634fb042481 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc @@ -10,19 +10,16 @@ #include <set> #include <utility> +#include "base/containers/flat_set.h" #include "base/logging.h" #include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/rect_conversions.h" #include "ui/ozone/platform/drm/gpu/drm_device.h" #include "ui/ozone/platform/drm/gpu/drm_framebuffer.h" #include "ui/ozone/platform/drm/gpu/drm_gpu_util.h" #include "ui/ozone/platform/drm/gpu/hardware_display_plane.h" namespace ui { -namespace { - -constexpr float kFixedPointScaleValue = 1 << 16; - -} // namespace HardwareDisplayPlaneList::HardwareDisplayPlaneList() { atomic_property_set.reset(drmModeAtomicAlloc()); @@ -51,12 +48,14 @@ HardwareDisplayPlaneManager::HardwareDisplayPlaneManager(DrmDevice* drm) HardwareDisplayPlaneManager::~HardwareDisplayPlaneManager() = default; bool HardwareDisplayPlaneManager::Initialize() { -// Try to get all of the planes if possible, so we don't have to try to -// discover hidden primary planes. -#if defined(DRM_CLIENT_CAP_UNIVERSAL_PLANES) + // Try to get all of the planes if possible, so we don't have to try to + // discover hidden primary planes. has_universal_planes_ = drm_->SetCapability(DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); -#endif + + // This is to test whether or not it is safe to remove non-universal planes + // supporting code in a following CL. See crbug.com/1129546 for more details. + CHECK(has_universal_planes_); if (!InitializeCrtcState()) return false; @@ -113,7 +112,7 @@ int HardwareDisplayPlaneManager::LookupConnectorIndex( bool HardwareDisplayPlaneManager::IsCompatible(HardwareDisplayPlane* plane, const DrmOverlayPlane& overlay, uint32_t crtc_index) const { - if (plane->type() == HardwareDisplayPlane::kCursor || + if (plane->type() == DRM_PLANE_TYPE_CURSOR || !plane->CanUseForCrtc(crtc_index)) return false; @@ -154,6 +153,19 @@ void HardwareDisplayPlaneManager::ResetCurrentPlaneList( plane_list->atomic_property_set.reset(drmModeAtomicAlloc()); } +void HardwareDisplayPlaneManager::RestoreCurrentPlaneList( + HardwareDisplayPlaneList* plane_list) const { + for (auto* plane : plane_list->plane_list) { + plane->set_in_use(false); + } + for (auto* plane : plane_list->old_plane_list) { + plane->set_in_use(true); + } + plane_list->plane_list.clear(); + plane_list->legacy_page_flips.clear(); + plane_list->atomic_property_set.reset(drmModeAtomicAlloc()); +} + void HardwareDisplayPlaneManager::BeginFrame( HardwareDisplayPlaneList* plane_list) { for (auto* plane : plane_list->old_plane_list) { @@ -176,30 +188,23 @@ bool HardwareDisplayPlaneManager::AssignOverlayPlanes( HardwareDisplayPlane* hw_plane = FindNextUnusedPlane(&plane_idx, crtc_index, plane); if (!hw_plane) { - LOG(ERROR) << "Failed to find a free plane for crtc " << crtc_id; - ResetCurrentPlaneList(plane_list); + RestoreCurrentPlaneList(plane_list); return false; } gfx::Rect fixed_point_rect; - if (hw_plane->type() != HardwareDisplayPlane::kDummy) { - const gfx::Size& size = plane.buffer->size(); - gfx::RectF crop_rect = plane.crop_rect; - crop_rect.Scale(size.width(), size.height()); - - // This returns a number in 16.16 fixed point, required by the DRM overlay - // APIs. - auto to_fixed_point = [](double v) -> uint32_t { - return v * kFixedPointScaleValue; - }; - fixed_point_rect = gfx::Rect(to_fixed_point(crop_rect.x()), - to_fixed_point(crop_rect.y()), - to_fixed_point(crop_rect.width()), - to_fixed_point(crop_rect.height())); - } + const gfx::Size& size = plane.buffer->size(); + gfx::RectF crop_rectf = plane.crop_rect; + crop_rectf.Scale(size.width(), size.height()); + // DrmOverlayManager::CanHandleCandidate guarantees this is safe. + gfx::Rect crop_rect = gfx::ToNearestRect(crop_rectf); + // Convert to 16.16 fixed point required by the DRM overlay APIs. + fixed_point_rect = + gfx::Rect(crop_rect.x() << 16, crop_rect.y() << 16, + crop_rect.width() << 16, crop_rect.height() << 16); if (!SetPlaneData(plane_list, hw_plane, plane, crtc_id, fixed_point_rect)) { - ResetCurrentPlaneList(plane_list); + RestoreCurrentPlaneList(plane_list); return false; } @@ -222,7 +227,7 @@ std::vector<uint64_t> HardwareDisplayPlaneManager::GetFormatModifiers( for (const auto& plane : planes_) { if (plane->CanUseForCrtc(crtc_index) && - plane->type() == HardwareDisplayPlane::kPrimary) { + plane->type() == DRM_PLANE_TYPE_PRIMARY) { return plane->ModifiersForFormat(format); } } @@ -345,6 +350,7 @@ bool HardwareDisplayPlaneManager::InitializeCrtcState() { return false; } + DisableConnectedConnectorsToCrtcs(resources); ResetConnectorsCache(resources); unsigned int num_crtcs_with_out_fence_ptr = 0; @@ -401,4 +407,85 @@ bool HardwareDisplayPlaneManager::InitializeCrtcState() { return true; } +void HardwareDisplayPlaneManager::DisableConnectedConnectorsToCrtcs( + const ScopedDrmResourcesPtr& resources) { + // Should only be called when no CRTC state has been set yet because we + // hard-disable CRTCs. + DCHECK(crtc_state_.empty()); + + for (int i = 0; i < resources->count_connectors; ++i) { + ScopedDrmConnectorPtr connector = + drm_->GetConnector(resources->connectors[i]); + if (!connector) + continue; + // Disable Zombie connectors (disconnected connectors but holding to an + // encoder). + if (connector->encoder_id && + connector->connection == DRM_MODE_DISCONNECTED) { + ScopedDrmEncoderPtr encoder( + drmModeGetEncoder(drm_->get_fd(), connector->encoder_id)); + if (encoder) + drm_->DisableCrtc(encoder->crtc_id); + } + } +} + +const HardwareDisplayPlaneManager::CrtcState& +HardwareDisplayPlaneManager::GetCrtcStateForCrtcId(uint32_t crtc_id) { + return CrtcStateForCrtcId(crtc_id); +} + +HardwareDisplayPlaneManager::CrtcState& +HardwareDisplayPlaneManager::CrtcStateForCrtcId(uint32_t crtc_id) { + int crtc_index = LookupCrtcIndex(crtc_id); + DCHECK_GE(crtc_index, 0); + return crtc_state_[crtc_index]; +} + +void HardwareDisplayPlaneManager::UpdateCrtcAndPlaneStatesAfterModeset( + const CommitRequest& commit_request) { + base::flat_set<HardwareDisplayPlaneList*> disable_planes_lists; + + for (const auto& crtc_request : commit_request) { + bool is_enabled = crtc_request.should_enable(); + + int connector_index = LookupConnectorIndex(crtc_request.connector_id()); + DCHECK_GE(connector_index, 0); + ConnectorProperties& connector_props = connectors_props_[connector_index]; + connector_props.crtc_id.value = is_enabled ? crtc_request.crtc_id() : 0; + + CrtcState& crtc_state = CrtcStateForCrtcId(crtc_request.crtc_id()); + crtc_state.properties.active.value = static_cast<uint64_t>(is_enabled); + + if (is_enabled) { + crtc_state.mode = crtc_request.mode(); + crtc_state.modeset_framebuffer = + DrmOverlayPlane::GetPrimaryPlane(crtc_request.overlays())->buffer; + } else { + if (crtc_request.plane_list()) + disable_planes_lists.insert(crtc_request.plane_list()); + + // TODO(crbug/1135291): Use atomic APIs to reset cursor plane. + if (!drm_->SetCursor(crtc_request.crtc_id(), 0, gfx::Size())) { + PLOG(ERROR) << "Failed to drmModeSetCursor: device:" + << drm_->device_path().value() + << " crtc:" << crtc_request.crtc_id(); + } + } + } + + // TODO(markyacoub): DisableOverlayPlanes should be part of the commit + // request. + for (HardwareDisplayPlaneList* list : disable_planes_lists) { + bool status = DisableOverlayPlanes(list); + LOG_IF(ERROR, !status) << "Can't disable overlays when disabling HDC."; + list->plane_list.clear(); + } +} + +void HardwareDisplayPlaneManager::ResetModesetBufferOfCrtc(uint32_t crtc_id) { + CrtcState& crtc_state = CrtcStateForCrtcId(crtc_id); + crtc_state.modeset_framebuffer = nullptr; +} + } // namespace ui diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h index b2443c90b07..dd25a23e0b1 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h @@ -14,6 +14,7 @@ #include "base/macros.h" #include "ui/display/types/gamma_ramp_rgb_entry.h" #include "ui/ozone/platform/drm/common/scoped_drm_types.h" +#include "ui/ozone/platform/drm/gpu/crtc_commit_request.h" #include "ui/ozone/platform/drm/gpu/drm_device.h" #include "ui/ozone/platform/drm/gpu/drm_overlay_plane.h" #include "ui/ozone/public/swap_completion_callback.h" @@ -56,6 +57,42 @@ struct HardwareDisplayPlaneList { class HardwareDisplayPlaneManager { public: + struct CrtcProperties { + // Unique identifier for the CRTC. This must be greater than 0 to be valid. + uint32_t id; + // Keeps track of the CRTC state. If a surface has been bound, then the + // value is set to true. Otherwise it is false. + DrmDevice::Property active; + DrmDevice::Property mode_id; + // Optional properties. + DrmDevice::Property ctm; + DrmDevice::Property gamma_lut; + DrmDevice::Property gamma_lut_size; + DrmDevice::Property degamma_lut; + DrmDevice::Property degamma_lut_size; + DrmDevice::Property out_fence_ptr; + DrmDevice::Property background_color; + }; + + struct CrtcState { + CrtcState(); + ~CrtcState(); + CrtcState(const CrtcState&) = delete; + CrtcState& operator=(const CrtcState&) = delete; + CrtcState(CrtcState&&); + + drmModeModeInfo mode = {}; + scoped_refptr<DrmFramebuffer> modeset_framebuffer; + + CrtcProperties properties = {}; + + // Cached blobs for the properties since the CRTC properties are applied on + // the next page flip and we need to keep the properties valid until then. + ScopedDrmPropertyBlob ctm_blob; + ScopedDrmPropertyBlob gamma_lut_blob; + ScopedDrmPropertyBlob degamma_lut_blob; + }; + explicit HardwareDisplayPlaneManager(DrmDevice* drm); virtual ~HardwareDisplayPlaneManager(); @@ -63,14 +100,13 @@ class HardwareDisplayPlaneManager { // or crtcs found. bool Initialize(); - // Performs modesetting, either atomic or legacy, depending on the device. - virtual bool Modeset(uint32_t crtc_id, - uint32_t framebuffer_id, - uint32_t connector_id, - const drmModeModeInfo& mode, - const HardwareDisplayPlaneList& plane_list) = 0; - - virtual bool DisableModeset(uint32_t crtc_id, uint32_t connector) = 0; + // |commit_request| contains all the necessary information to build the + // atomic/legacy request. It acts as a thin wrapper that looks like the atomic + // request. It then gets converted into an atomic request for DRM atomic and + // has all the parameters for a legacy request. + // TODO(markyacoub): Consolidate this Commit() with the overloaded page flip + // Commit() down below. + virtual bool Commit(CommitRequest commit_request, uint32_t flags) = 0; // Clears old frame state out. Must be called before any AssignOverlayPlanes // calls. @@ -97,7 +133,7 @@ class HardwareDisplayPlaneManager { uint32_t crtc_id); // Commit the plane states in |plane_list|. - // + // if |should_modeset| is set, it only modesets without page flipping. // If |page_flip_request| is null, this tests the plane configuration without // submitting it. // The fence returned in |out_fence| will signal when the currently scanned @@ -105,6 +141,7 @@ class HardwareDisplayPlaneManager { // |page_flip_request|. Note that the returned fence may be a nullptr // if the system doesn't support out fences. virtual bool Commit(HardwareDisplayPlaneList* plane_list, + bool should_modeset, scoped_refptr<PageFlipRequest> page_flip_request, std::unique_ptr<gfx::GpuFence>* out_fence) = 0; @@ -145,44 +182,35 @@ class HardwareDisplayPlaneManager { // called whenever a DRM hotplug event is received via UDEV. void ResetConnectorsCache(const ScopedDrmResourcesPtr& resources); + // Get Immutable CRTC State. + const CrtcState& GetCrtcStateForCrtcId(uint32_t crtc_id); + + // TODO(markyacoub): this seems hacky, this could be cleaned up a bit. Clarify + // which resources needed to be tracked internally in + // HardwareDisplayPlaneManager and which should be taken care of by the + // caller. + void ResetModesetBufferOfCrtc(uint32_t crtc_id); + protected: struct ConnectorProperties { uint32_t id; DrmDevice::Property crtc_id; }; - struct CrtcProperties { - // Unique identifier for the CRTC. This must be greater than 0 to be valid. - uint32_t id; - DrmDevice::Property active; - DrmDevice::Property mode_id; - // Optional properties. - DrmDevice::Property ctm; - DrmDevice::Property gamma_lut; - DrmDevice::Property gamma_lut_size; - DrmDevice::Property degamma_lut; - DrmDevice::Property degamma_lut_size; - DrmDevice::Property out_fence_ptr; - DrmDevice::Property background_color; - }; - - struct CrtcState { - CrtcState(); - ~CrtcState(); - CrtcState(CrtcState&&); - - CrtcProperties properties = {}; - - // Cached blobs for the properties since the CRTC properties are applied on - // the next page flip and we need to keep the properties valid until then. - ScopedDrmPropertyBlob ctm_blob; - ScopedDrmPropertyBlob gamma_lut_blob; - ScopedDrmPropertyBlob degamma_lut_blob; + bool InitializeCrtcState(); - DISALLOW_COPY_AND_ASSIGN(CrtcState); - }; + void UpdateCrtcAndPlaneStatesAfterModeset( + const CommitRequest& commit_request); - bool InitializeCrtcState(); + // As the CRTC is being initialized, all connectors connected to it should + // be disabled. This is a workaround for a bug on Hatch where Puff enables + // a connector in dev mode before Chrome even starts. The kernel maps the HW + // state at initial modeset (with a dangling connector attached to a CRTC). + // When an Atomic Modeset is performed, it fails to modeset as the CRTC is + // already attached to another dead connector. (Analysis: crbug/1067121#c5) + // TODO(b/168154314): Remove this call when the bug is fixed. + void DisableConnectedConnectorsToCrtcs( + const ScopedDrmResourcesPtr& resources); virtual bool InitializePlanes() = 0; @@ -206,13 +234,22 @@ class HardwareDisplayPlaneManager { int LookupCrtcIndex(uint32_t crtc_id) const; int LookupConnectorIndex(uint32_t connector_idx) const; + // Get Mutable CRTC State. + CrtcState& CrtcStateForCrtcId(uint32_t crtc_id); + // Returns true if |plane| can support |overlay| and compatible with // |crtc_index|. virtual bool IsCompatible(HardwareDisplayPlane* plane, const DrmOverlayPlane& overlay, uint32_t crtc_index) const; + // Resets |plane_list| setting all planes to unused. + // Frees any temporary data structure in |plane_list| used for pageflipping. void ResetCurrentPlaneList(HardwareDisplayPlaneList* plane_list) const; + // Restores |plane_list| planes |in_use| flag to what it was before + // BeginFrame was called. + // Frees any temporary data structure in |plane_list| used for pageflipping. + void RestoreCurrentPlaneList(HardwareDisplayPlaneList* plane_list) const; // Populates scanout formats supported by all planes. void PopulateSupportedFormats(); diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc index 2bdf60aba9d..7c3a287b4ca 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc @@ -11,6 +11,7 @@ #include <utility> #include "base/bind.h" +#include "base/containers/flat_set.h" #include "base/files/platform_file.h" #include "base/logging.h" #include "base/stl_util.h" @@ -44,14 +45,25 @@ std::unique_ptr<gfx::GpuFence> CreateMergedGpuFenceFromFDs( if (merged_fd.is_valid()) { gfx::GpuFenceHandle handle; - handle.type = gfx::GpuFenceHandleType::kAndroidNativeFenceSync; - handle.native_fd = base::FileDescriptor(std::move(merged_fd)); - return std::make_unique<gfx::GpuFence>(handle); + handle.owned_fd = std::move(merged_fd); + return std::make_unique<gfx::GpuFence>(std::move(handle)); } return nullptr; } +std::vector<uint32_t> GetCrtcIdsOfPlanes( + const HardwareDisplayPlaneList& plane_list) { + std::vector<uint32_t> crtcs; + for (HardwareDisplayPlane* plane : plane_list.plane_list) { + HardwareDisplayPlaneAtomic* atomic_plane = + static_cast<HardwareDisplayPlaneAtomic*>(plane); + if (crtcs.empty() || crtcs.back() != atomic_plane->AssignedCrtcId()) + crtcs.push_back(atomic_plane->AssignedCrtcId()); + } + return crtcs; +} + } // namespace HardwareDisplayPlaneManagerAtomic::HardwareDisplayPlaneManagerAtomic( @@ -61,66 +73,144 @@ HardwareDisplayPlaneManagerAtomic::HardwareDisplayPlaneManagerAtomic( HardwareDisplayPlaneManagerAtomic::~HardwareDisplayPlaneManagerAtomic() = default; -bool HardwareDisplayPlaneManagerAtomic::Modeset( +bool HardwareDisplayPlaneManagerAtomic::SetCrtcProps( + drmModeAtomicReq* atomic_request, uint32_t crtc_id, - uint32_t framebuffer_id, + bool set_active, + uint32_t mode_id) { + // Only making a copy here to retrieve the the props IDs. The state will be + // updated only after a successful modeset. + CrtcProperties modeset_props = GetCrtcStateForCrtcId(crtc_id).properties; + modeset_props.active.value = static_cast<uint64_t>(set_active); + modeset_props.mode_id.value = mode_id; + + bool status = + AddPropertyIfValid(atomic_request, crtc_id, modeset_props.active); + status &= AddPropertyIfValid(atomic_request, crtc_id, modeset_props.mode_id); + return status; +} + +bool HardwareDisplayPlaneManagerAtomic::SetConnectorProps( + drmModeAtomicReq* atomic_request, uint32_t connector_id, - const drmModeModeInfo& mode, - const HardwareDisplayPlaneList&) { - return drm_->SetCrtc(crtc_id, framebuffer_id, - std::vector<uint32_t>(1, connector_id), mode); + uint32_t crtc_id) { + int connector_index = LookupConnectorIndex(connector_id); + DCHECK_GE(connector_index, 0); + // Only making a copy here to retrieve the the props IDs. The state will be + // updated only after a successful modeset. + ConnectorProperties connector_props = connectors_props_[connector_index]; + connector_props.crtc_id.value = crtc_id; + + return AddPropertyIfValid(atomic_request, connector_id, + connector_props.crtc_id); } -bool HardwareDisplayPlaneManagerAtomic::DisableModeset(uint32_t crtc_id, - uint32_t connector) { - ScopedDrmAtomicReqPtr property_set(drmModeAtomicAlloc()); +bool HardwareDisplayPlaneManagerAtomic::Commit(CommitRequest commit_request, + uint32_t flags) { + bool is_testing = flags & DRM_MODE_ATOMIC_TEST_ONLY; + bool status = true; + + std::vector<ScopedDrmPropertyBlob> scoped_blobs; + + base::flat_set<HardwareDisplayPlaneList*> enable_planes_lists; + base::flat_set<HardwareDisplayPlaneList*> all_planes_lists; + + ScopedDrmAtomicReqPtr atomic_request(drmModeAtomicAlloc()); + + for (const auto& crtc_request : commit_request) { + if (crtc_request.plane_list()) + all_planes_lists.insert(crtc_request.plane_list()); + + uint32_t mode_id = 0; + if (crtc_request.should_enable()) { + auto mode_blob = drm_->CreatePropertyBlob(&crtc_request.mode(), + sizeof(crtc_request.mode())); + status &= (mode_blob != nullptr); + if (mode_blob) { + scoped_blobs.push_back(std::move(mode_blob)); + mode_id = scoped_blobs.back()->id(); + } + } - const int connector_idx = LookupConnectorIndex(connector); - DCHECK_GE(connector_idx, 0); - connectors_props_[connector_idx].crtc_id.value = 0UL; - bool res = AddPropertyIfValid(property_set.get(), connector, - connectors_props_[connector_idx].crtc_id); - - const int crtc_idx = LookupCrtcIndex(crtc_id); - DCHECK_GE(crtc_idx, 0); - crtc_state_[crtc_idx].properties.active.value = 0UL; - crtc_state_[crtc_idx].properties.mode_id.value = 0UL; - res &= AddPropertyIfValid(property_set.get(), crtc_id, - crtc_state_[crtc_idx].properties.active); - res &= AddPropertyIfValid(property_set.get(), crtc_id, - crtc_state_[crtc_idx].properties.mode_id); - - DCHECK(res); - return drm_->CommitProperties(property_set.get(), - DRM_MODE_ATOMIC_ALLOW_MODESET, 1, nullptr); + uint32_t crtc_id = crtc_request.crtc_id(); + + status &= SetCrtcProps(atomic_request.get(), crtc_id, + crtc_request.should_enable(), mode_id); + status &= + SetConnectorProps(atomic_request.get(), crtc_request.connector_id(), + crtc_request.should_enable() * crtc_id); + + if (crtc_request.should_enable()) { + DCHECK(crtc_request.plane_list()); + status &= AssignOverlayPlanes(crtc_request.plane_list(), + crtc_request.overlays(), crtc_id); + enable_planes_lists.insert(crtc_request.plane_list()); + } + } + + // TODO(markyacoub): Ideally this doesn't need to be a separate step. It + // should all be handled in Set{Crtc,Connector,Plane}Props() modulo some state + // tracking changes that should be done post commit. Break it apart when both + // Commit() are consolidated. + for (HardwareDisplayPlaneList* list : enable_planes_lists) { + SetAtomicPropsForCommit(atomic_request.get(), list, + GetCrtcIdsOfPlanes(*list), is_testing); + } + + // TODO(markyacoub): failed |status|'s should be made as DCHECKs. The only + // reason some of these would be failing is OOM. If we OOM-ed there's no point + // in trying to recover. + if (!status || !drm_->CommitProperties(atomic_request.get(), flags, + commit_request.size(), nullptr)) { + if (is_testing) + VPLOG(2) << "Modeset Test is rejected."; + else + PLOG(ERROR) << "Failed to commit properties for modeset."; + + for (HardwareDisplayPlaneList* list : all_planes_lists) + ResetCurrentPlaneList(list); + + return false; + } + + if (!is_testing) + UpdateCrtcAndPlaneStatesAfterModeset(commit_request); + + for (HardwareDisplayPlaneList* list : enable_planes_lists) + list->plane_list.clear(); + + return true; } -bool HardwareDisplayPlaneManagerAtomic::Commit( +void HardwareDisplayPlaneManagerAtomic::SetAtomicPropsForCommit( + drmModeAtomicReq* atomic_request, HardwareDisplayPlaneList* plane_list, - scoped_refptr<PageFlipRequest> page_flip_request, - std::unique_ptr<gfx::GpuFence>* out_fence) { - bool test_only = !page_flip_request; + const std::vector<uint32_t>& crtcs, + bool test_only) { + for (HardwareDisplayPlane* plane : plane_list->plane_list) { + HardwareDisplayPlaneAtomic* atomic_plane = + static_cast<HardwareDisplayPlaneAtomic*>(plane); + atomic_plane->SetPlaneProps(atomic_request); + } + for (HardwareDisplayPlane* plane : plane_list->old_plane_list) { if (!base::Contains(plane_list->plane_list, plane)) { - // This plane is being released, so we need to zero it. + // |plane| is shared state between |old_plane_list| and |plane_list|. + // When we call BeginFrame(), we reset in_use since we need to be able to + // allocate the planes as needed. The current frame might not need to use + // |plane|, thus |plane->in_use()| would be false even though the previous + // frame used it. It's existence in |old_plane_list| is sufficient to + // signal that |plane| was in use previously. plane->set_in_use(false); HardwareDisplayPlaneAtomic* atomic_plane = static_cast<HardwareDisplayPlaneAtomic*>(plane); - atomic_plane->SetPlaneData( - plane_list->atomic_property_set.get(), 0, 0, gfx::Rect(), gfx::Rect(), - gfx::OVERLAY_TRANSFORM_NONE, base::kInvalidPlatformFile); + atomic_plane->AssignPlaneProps(0, 0, gfx::Rect(), gfx::Rect(), + gfx::OVERLAY_TRANSFORM_NONE, + base::kInvalidPlatformFile); + atomic_plane->SetPlaneProps(atomic_request); } } - std::vector<uint32_t> crtcs; - for (HardwareDisplayPlane* plane : plane_list->plane_list) { - HardwareDisplayPlaneAtomic* atomic_plane = - static_cast<HardwareDisplayPlaneAtomic*>(plane); - if (crtcs.empty() || crtcs.back() != atomic_plane->crtc_id()) - crtcs.push_back(atomic_plane->crtc_id()); - } - - drmModeAtomicReqPtr request = plane_list->atomic_property_set.get(); for (uint32_t crtc : crtcs) { int idx = LookupCrtcIndex(crtc); @@ -129,29 +219,40 @@ bool HardwareDisplayPlaneManagerAtomic::Commit( // swap chain for a vsync. // TODO(dnicoara): See if we can apply these properties async using // DRM_MODE_ATOMIC_ASYNC_UPDATE flag when committing. - AddPropertyIfValid(request, crtc, crtc_state_[idx].properties.degamma_lut); - AddPropertyIfValid(request, crtc, crtc_state_[idx].properties.gamma_lut); - AddPropertyIfValid(request, crtc, crtc_state_[idx].properties.ctm); + AddPropertyIfValid(atomic_request, crtc, + crtc_state_[idx].properties.degamma_lut); + AddPropertyIfValid(atomic_request, crtc, + crtc_state_[idx].properties.gamma_lut); + AddPropertyIfValid(atomic_request, crtc, crtc_state_[idx].properties.ctm); #endif - AddPropertyIfValid(request, crtc, + AddPropertyIfValid(atomic_request, crtc, crtc_state_[idx].properties.background_color); } if (test_only) { - for (HardwareDisplayPlane* plane : plane_list->plane_list) { + for (auto* plane : plane_list->plane_list) { plane->set_in_use(false); } + for (auto* plane : plane_list->old_plane_list) { + plane->set_in_use(true); + } } else { plane_list->plane_list.swap(plane_list->old_plane_list); } +} - uint32_t flags = 0; - if (test_only) { - flags = DRM_MODE_ATOMIC_TEST_ONLY; - } else { - flags = DRM_MODE_ATOMIC_NONBLOCK; - } +bool HardwareDisplayPlaneManagerAtomic::Commit( + HardwareDisplayPlaneList* plane_list, + bool should_modeset, + scoped_refptr<PageFlipRequest> page_flip_request, + std::unique_ptr<gfx::GpuFence>* out_fence) { + bool test_only = !should_modeset && !page_flip_request; + + std::vector<uint32_t> crtcs = GetCrtcIdsOfPlanes(*plane_list); + + SetAtomicPropsForCommit(plane_list->atomic_property_set.get(), plane_list, + crtcs, test_only); // After we perform the atomic commit, and if the caller has requested an // out-fence, the out_fence_fds vector will contain any provided out-fence @@ -172,6 +273,12 @@ bool HardwareDisplayPlaneManagerAtomic::Commit( } } + uint32_t flags = 0; + if (should_modeset) + flags = DRM_MODE_ATOMIC_ALLOW_MODESET; + else + flags = test_only ? DRM_MODE_ATOMIC_TEST_ONLY : DRM_MODE_ATOMIC_NONBLOCK; + if (!drm_->CommitProperties(plane_list->atomic_property_set.get(), flags, crtcs.size(), page_flip_request)) { if (!test_only) { @@ -196,16 +303,15 @@ bool HardwareDisplayPlaneManagerAtomic::Commit( bool HardwareDisplayPlaneManagerAtomic::DisableOverlayPlanes( HardwareDisplayPlaneList* plane_list) { for (HardwareDisplayPlane* plane : plane_list->old_plane_list) { - if (plane->type() != HardwareDisplayPlane::kOverlay) - continue; plane->set_in_use(false); plane->set_owning_crtc(0); HardwareDisplayPlaneAtomic* atomic_plane = static_cast<HardwareDisplayPlaneAtomic*>(plane); - atomic_plane->SetPlaneData( - plane_list->atomic_property_set.get(), 0, 0, gfx::Rect(), gfx::Rect(), - gfx::OVERLAY_TRANSFORM_NONE, base::kInvalidPlatformFile); + atomic_plane->AssignPlaneProps(0, 0, gfx::Rect(), gfx::Rect(), + gfx::OVERLAY_TRANSFORM_NONE, + base::kInvalidPlatformFile); + atomic_plane->SetPlaneProps(plane_list->atomic_property_set.get()); } bool ret = drm_->CommitProperties(plane_list->atomic_property_set.get(), DRM_MODE_ATOMIC_NONBLOCK, 0, nullptr); @@ -273,19 +379,12 @@ bool HardwareDisplayPlaneManagerAtomic::SetPlaneData( if (overlay.gpu_fence) { const auto& gpu_fence_handle = overlay.gpu_fence->GetGpuFenceHandle(); - if (gpu_fence_handle.type != - gfx::GpuFenceHandleType::kAndroidNativeFenceSync) { - LOG(ERROR) << "Received invalid gpu fence"; - return false; - } - fence_fd = gpu_fence_handle.native_fd.fd; + fence_fd = gpu_fence_handle.owned_fd.get(); } - if (!atomic_plane->SetPlaneData(plane_list->atomic_property_set.get(), - crtc_id, framebuffer_id, - overlay.display_bounds, src_rect, - overlay.plane_transform, fence_fd)) { - LOG(ERROR) << "Failed to set plane properties"; + if (!atomic_plane->AssignPlaneProps(crtc_id, framebuffer_id, + overlay.display_bounds, src_rect, + overlay.plane_transform, fence_fd)) { return false; } return true; @@ -376,7 +475,7 @@ bool HardwareDisplayPlaneManagerAtomic::CommitGammaCorrection( } bool HardwareDisplayPlaneManagerAtomic::AddOutFencePtrProperties( - drmModeAtomicReqPtr property_set, + drmModeAtomicReq* property_set, const std::vector<uint32_t>& crtcs, std::vector<base::ScopedFD>* out_fence_fds, std::vector<base::ScopedFD::Receiver>* out_fence_fd_receivers) { diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.h b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.h index 7567a9bab24..b6d717746b3 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.h +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.h @@ -19,13 +19,10 @@ class HardwareDisplayPlaneManagerAtomic : public HardwareDisplayPlaneManager { ~HardwareDisplayPlaneManagerAtomic() override; // HardwareDisplayPlaneManager: - bool Modeset(uint32_t crtc_id, - uint32_t framebuffer_id, - uint32_t connector_id, - const drmModeModeInfo& mode, - const HardwareDisplayPlaneList& plane_list) override; - bool DisableModeset(uint32_t crtc_id, uint32_t connector) override; + bool Commit(CommitRequest commit_request, uint32_t flags) override; + bool Commit(HardwareDisplayPlaneList* plane_list, + bool should_modeset, scoped_refptr<PageFlipRequest> page_flip_request, std::unique_ptr<gfx::GpuFence>* out_fence) override; bool DisableOverlayPlanes(HardwareDisplayPlaneList* plane_list) override; @@ -49,10 +46,23 @@ class HardwareDisplayPlaneManagerAtomic : public HardwareDisplayPlaneManager { private: bool InitializePlanes() override; std::unique_ptr<HardwareDisplayPlane> CreatePlane(uint32_t plane_id) override; + void SetAtomicPropsForCommit(drmModeAtomicReq* atomic_request, + HardwareDisplayPlaneList* plane_list, + const std::vector<uint32_t>& crtcs, + bool test_only); + + bool SetCrtcProps(drmModeAtomicReq* atomic_request, + uint32_t crtc_id, + bool set_active, + uint32_t mode_id); + bool SetConnectorProps(drmModeAtomicReq* atomic_request, + uint32_t connector_id, + uint32_t crtc_id); + bool CommitColorMatrix(const CrtcProperties& crtc_props) override; bool CommitGammaCorrection(const CrtcProperties& crtc_props) override; bool AddOutFencePtrProperties( - drmModeAtomicReqPtr property_set, + drmModeAtomicReq* property_set, const std::vector<uint32_t>& crtcs, std::vector<base::ScopedFD>* out_fence_fds, std::vector<base::ScopedFD::Receiver>* out_fence_fd_receivers); diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.cc b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.cc index 91926a0d2b2..298908088a0 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.cc +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.cc @@ -20,7 +20,6 @@ #include "ui/ozone/platform/drm/gpu/drm_device.h" #include "ui/ozone/platform/drm/gpu/drm_framebuffer.h" #include "ui/ozone/platform/drm/gpu/hardware_display_plane.h" -#include "ui/ozone/platform/drm/gpu/hardware_display_plane_dummy.h" #include "ui/ozone/platform/drm/gpu/page_flip_request.h" namespace ui { @@ -47,25 +46,43 @@ HardwareDisplayPlaneManagerLegacy::HardwareDisplayPlaneManagerLegacy( HardwareDisplayPlaneManagerLegacy::~HardwareDisplayPlaneManagerLegacy() = default; -bool HardwareDisplayPlaneManagerLegacy::Modeset( - uint32_t crtc_id, - uint32_t framebuffer_id, - uint32_t connector_id, - const drmModeModeInfo& mode, - const HardwareDisplayPlaneList&) { - return drm_->SetCrtc(crtc_id, framebuffer_id, - std::vector<uint32_t>(1, connector_id), mode); -} +bool HardwareDisplayPlaneManagerLegacy::Commit(CommitRequest commit_request, + uint32_t flags) { + if (flags & DRM_MODE_ATOMIC_TEST_ONLY) + // Legacy DRM does not support testing. + return true; + + bool status = true; + for (const auto& crtc_request : commit_request) { + if (crtc_request.should_enable()) { + // Overlays are not supported in legacy hence why we're only looking at + // the primary plane. + uint32_t fb_id = DrmOverlayPlane::GetPrimaryPlane(crtc_request.overlays()) + ->buffer->opaque_framebuffer_id(); + status &= + drm_->SetCrtc(crtc_request.crtc_id(), fb_id, + std::vector<uint32_t>(1, crtc_request.connector_id()), + crtc_request.mode()); + } else { + drm_->DisableCrtc(crtc_request.crtc_id()); + } + } -bool HardwareDisplayPlaneManagerLegacy::DisableModeset(uint32_t crtc_id, - uint32_t connector) { - return drm_->DisableCrtc(crtc_id); + if (status) + UpdateCrtcAndPlaneStatesAfterModeset(commit_request); + + return status; } bool HardwareDisplayPlaneManagerLegacy::Commit( HardwareDisplayPlaneList* plane_list, + bool should_modeset, scoped_refptr<PageFlipRequest> page_flip_request, std::unique_ptr<gfx::GpuFence>* out_fence) { + // Legacy Modeset should not call Commit. Ensure the separation between both + // Atomic and Legacy and nothing trickles in. + DCHECK(!should_modeset); + bool test_only = !page_flip_request; if (test_only) { for (HardwareDisplayPlane* plane : plane_list->plane_list) { @@ -114,7 +131,7 @@ bool HardwareDisplayPlaneManagerLegacy::DisableOverlayPlanes( DCHECK(std::find_if(plane_list->old_plane_list.begin(), plane_list->old_plane_list.end(), [](HardwareDisplayPlane* plane) { - return plane->type() == HardwareDisplayPlane::kOverlay; + return plane->type() == DRM_PLANE_TYPE_OVERLAY; }) == plane_list->old_plane_list.end()); return true; } @@ -161,31 +178,12 @@ bool HardwareDisplayPlaneManagerLegacy::InitializePlanes() { // Overlays are not supported on the legacy path, so ignore all overlay // planes. - if (plane->type() == HardwareDisplayPlane::kOverlay) + if (plane->type() == DRM_PLANE_TYPE_OVERLAY) continue; planes_.push_back(std::move(plane)); } - // https://crbug.com/464085: if driver reports no primary planes for a crtc, - // create a dummy plane for which we can assign exactly one overlay. - if (!has_universal_planes_) { - for (size_t i = 0; i < crtc_state_.size(); ++i) { - uint32_t id = crtc_state_[i].properties.id - 1; - if (std::find_if( - planes_.begin(), planes_.end(), - [id](const std::unique_ptr<HardwareDisplayPlane>& plane) { - return plane->id() == id; - }) == planes_.end()) { - std::unique_ptr<HardwareDisplayPlane> dummy_plane( - new HardwareDisplayPlaneDummy(id, 1 << i)); - if (dummy_plane->Initialize(drm_)) { - planes_.push_back(std::move(dummy_plane)); - } - } - } - } - return true; } @@ -201,9 +199,8 @@ bool HardwareDisplayPlaneManagerLegacy::SetPlaneData( if (plane_list->legacy_page_flips.empty() || plane_list->legacy_page_flips.back().crtc_id != crtc_id) { - plane_list->legacy_page_flips.push_back( - HardwareDisplayPlaneList::PageFlipInfo( - crtc_id, overlay.buffer->opaque_framebuffer_id())); + plane_list->legacy_page_flips.emplace_back( + crtc_id, overlay.buffer->opaque_framebuffer_id()); } else { return false; } @@ -215,7 +212,7 @@ bool HardwareDisplayPlaneManagerLegacy::IsCompatible( HardwareDisplayPlane* plane, const DrmOverlayPlane& overlay, uint32_t crtc_index) const { - if (plane->type() == HardwareDisplayPlane::kCursor || + if (plane->type() == DRM_PLANE_TYPE_CURSOR || !plane->CanUseForCrtc(crtc_index)) return false; diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h index 237689dd288..5d03f971f37 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h @@ -19,13 +19,10 @@ class HardwareDisplayPlaneManagerLegacy : public HardwareDisplayPlaneManager { ~HardwareDisplayPlaneManagerLegacy() override; // HardwareDisplayPlaneManager: - bool Modeset(uint32_t crtc_id, - uint32_t framebuffer_id, - uint32_t connector_id, - const drmModeModeInfo& mode, - const HardwareDisplayPlaneList& plane_list) override; - bool DisableModeset(uint32_t crtc_id, uint32_t connector) override; + bool Commit(CommitRequest commit_request, uint32_t flags) override; + bool Commit(HardwareDisplayPlaneList* plane_list, + bool should_modeset, scoped_refptr<PageFlipRequest> page_flip_request, std::unique_ptr<gfx::GpuFence>* out_fence) override; bool DisableOverlayPlanes(HardwareDisplayPlaneList* plane_list) override; diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc index 798ed118db5..4d967114368 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc @@ -37,6 +37,7 @@ constexpr uint32_t kCrtcIdBase = 500; constexpr uint32_t kConnectorIdBase = 700; constexpr uint32_t kActivePropId = 1000; +constexpr uint32_t kModePropId = 1001; constexpr uint32_t kBackgroundColorPropId = 1002; constexpr uint32_t kCtmPropId = 1003; constexpr uint32_t kGammaLutPropId = 1004; @@ -121,7 +122,7 @@ void HardwareDisplayPlaneManagerTest::InitializeDrmState( size_t planes_per_crtc) { std::map<uint32_t, std::string> crtc_property_names = { {kActivePropId, "ACTIVE"}, - {1001, "MODE_ID"}, + {kModePropId, "MODE_ID"}, }; std::vector<ui::MockDrmDevice::ConnectorProperties> connector_properties(1); @@ -226,8 +227,8 @@ void HardwareDisplayPlaneManagerTest::PerformPageFlip( state, assigns, crtc_properties_[crtc_idx].id)); scoped_refptr<ui::PageFlipRequest> page_flip_request = base::MakeRefCounted<ui::PageFlipRequest>(base::TimeDelta()); - ASSERT_TRUE( - fake_drm_->plane_manager()->Commit(state, page_flip_request, nullptr)); + ASSERT_TRUE(fake_drm_->plane_manager()->Commit( + state, /*should_modeset*/ false, page_flip_request, nullptr)); } uint64_t HardwareDisplayPlaneManagerTest::GetObjectPropertyValue( @@ -275,13 +276,24 @@ TEST_P(HardwareDisplayPlaneManagerTest, ResettingConnectorCache) { plane_properties_, property_names_, /*use_atomic=*/true); - constexpr uint32_t kFrameBuffer = 2; ui::HardwareDisplayPlaneList state; - // Check all 3 connectors exist - for (size_t i = 0; i < connector_and_crtc_count; ++i) { - EXPECT_TRUE(fake_drm_->plane_manager()->Modeset( - crtc_properties_[i].id, kFrameBuffer, connector_properties[i].id, - kDefaultMode, state)); + + { + ui::CommitRequest commit_request; + fake_drm_->plane_manager()->BeginFrame(&state); + // Check all 3 connectors exist + for (size_t i = 0; i < connector_and_crtc_count; ++i) { + ui::DrmOverlayPlaneList overlays; + overlays.push_back(ui::DrmOverlayPlane(fake_buffer_, nullptr)); + + ui::CrtcCommitRequest request = ui::CrtcCommitRequest::EnableCrtcRequest( + crtc_properties_[i].id, connector_properties[i].id, kDefaultMode, + &state, std::move(overlays)); + commit_request.push_back(std::move(request)); + } + + EXPECT_TRUE(fake_drm_->plane_manager()->Commit( + std::move(commit_request), DRM_MODE_ATOMIC_ALLOW_MODESET)); } // Replace last connector and update state. @@ -290,17 +302,34 @@ TEST_P(HardwareDisplayPlaneManagerTest, ResettingConnectorCache) { plane_properties_, property_names_); fake_drm_->plane_manager()->ResetConnectorsCache(fake_drm_->GetResources()); - EXPECT_TRUE(fake_drm_->plane_manager()->Modeset( - crtc_properties_[0].id, kFrameBuffer, kConnectorIdBase, kDefaultMode, - state)); - EXPECT_TRUE(fake_drm_->plane_manager()->Modeset( - crtc_properties_[1].id, kFrameBuffer, kConnectorIdBase + 1, kDefaultMode, - state)); - // TODO(markyacoub): Add a test that fails for kConnectorIdBase +2 when atomic - // modeset is enabled. - EXPECT_TRUE(fake_drm_->plane_manager()->Modeset( - crtc_properties_[2].id, kFrameBuffer, kConnectorIdBase + 3, kDefaultMode, - state)); + { + ui::CommitRequest commit_request; + fake_drm_->plane_manager()->BeginFrame(&state); + { + ui::DrmOverlayPlaneList overlays; + overlays.push_back(ui::DrmOverlayPlane(fake_buffer_, nullptr)); + commit_request.push_back(ui::CrtcCommitRequest::EnableCrtcRequest( + crtc_properties_[0].id, kConnectorIdBase, kDefaultMode, &state, + std::move(overlays))); + } + { + ui::DrmOverlayPlaneList overlays; + overlays.push_back(ui::DrmOverlayPlane(fake_buffer_, nullptr)); + commit_request.push_back(ui::CrtcCommitRequest::EnableCrtcRequest( + crtc_properties_[1].id, kConnectorIdBase + 1, kDefaultMode, &state, + std::move(overlays))); + } + { + ui::DrmOverlayPlaneList overlays; + overlays.push_back(ui::DrmOverlayPlane(fake_buffer_, nullptr)); + commit_request.push_back(ui::CrtcCommitRequest::EnableCrtcRequest( + crtc_properties_[2].id, kConnectorIdBase + 3, kDefaultMode, &state, + std::move(overlays))); + } + + EXPECT_TRUE(fake_drm_->plane_manager()->Commit( + std::move(commit_request), DRM_MODE_ATOMIC_ALLOW_MODESET)); + } } TEST_P(HardwareDisplayPlaneManagerLegacyTest, Modeset) { @@ -310,12 +339,19 @@ TEST_P(HardwareDisplayPlaneManagerLegacyTest, Modeset) { /*use_atomic=*/false); fake_drm_->set_set_crtc_expectation(false); - constexpr uint32_t kFrameBuffer = 2; ui::HardwareDisplayPlaneList state; - EXPECT_FALSE(fake_drm_->plane_manager()->Modeset( - crtc_properties_[0].id, kFrameBuffer, connector_properties_[0].id, - kDefaultMode, state)); - EXPECT_EQ(kFrameBuffer, fake_drm_->current_framebuffer()); + ui::DrmOverlayPlane plane(fake_buffer_, nullptr); + ui::CommitRequest commit_request; + + ui::DrmOverlayPlaneList overlays; + overlays.push_back(plane.Clone()); + commit_request.push_back(ui::CrtcCommitRequest::EnableCrtcRequest( + crtc_properties_[0].id, connector_properties_[0].id, kDefaultMode, &state, + std::move(overlays))); + EXPECT_FALSE(fake_drm_->plane_manager()->Commit( + std::move(commit_request), DRM_MODE_ATOMIC_ALLOW_MODESET)); + + EXPECT_EQ(plane.buffer->framebuffer_id(), fake_drm_->current_framebuffer()); EXPECT_EQ(1, fake_drm_->get_set_crtc_call_count()); } @@ -325,8 +361,12 @@ TEST_P(HardwareDisplayPlaneManagerLegacyTest, DisableModeset) { plane_properties_, property_names_, /*use_atomic*/ false); - EXPECT_TRUE( - fake_drm_->plane_manager()->DisableModeset(crtc_properties_[0].id, 0)); + ui::HardwareDisplayPlaneList state; + ui::CommitRequest commit_request; + commit_request.push_back(ui::CrtcCommitRequest::DisableCrtcRequest( + crtc_properties_[0].id, connector_properties_[0].id, &state)); + EXPECT_TRUE(fake_drm_->plane_manager()->Commit( + std::move(commit_request), DRM_MODE_ATOMIC_ALLOW_MODESET)); } TEST_P(HardwareDisplayPlaneManagerLegacyTest, SinglePlaneAssignment) { @@ -352,7 +392,7 @@ TEST_P(HardwareDisplayPlaneManagerLegacyTest, AddCursor) { bool cursor_found = false; for (const auto& plane : fake_drm_->plane_manager()->planes()) { - if (plane->type() == ui::HardwareDisplayPlane::kCursor) { + if (plane->type() == DRM_PLANE_TYPE_CURSOR) { cursor_found = true; break; } @@ -449,13 +489,18 @@ TEST_P(HardwareDisplayPlaneManagerAtomicTest, Modeset) { plane_properties_, property_names_, /*use_atomic=*/true); - constexpr uint32_t kFrameBuffer = 2; ui::HardwareDisplayPlaneList state; - EXPECT_TRUE(fake_drm_->plane_manager()->Modeset( - crtc_properties_[0].id, kFrameBuffer, connector_properties_[0].id, - kDefaultMode, state)); + ui::CommitRequest commit_request; + ui::DrmOverlayPlaneList overlays; + overlays.push_back(ui::DrmOverlayPlane(fake_buffer_, nullptr)); - EXPECT_EQ(0, fake_drm_->get_commit_count()); + commit_request.push_back(ui::CrtcCommitRequest::EnableCrtcRequest( + crtc_properties_[0].id, connector_properties_[0].id, kDefaultMode, &state, + std::move(overlays))); + EXPECT_TRUE(fake_drm_->plane_manager()->Commit( + std::move(commit_request), DRM_MODE_ATOMIC_ALLOW_MODESET)); + + EXPECT_EQ(1, fake_drm_->get_commit_count()); } TEST_P(HardwareDisplayPlaneManagerAtomicTest, DisableModeset) { @@ -464,9 +509,52 @@ TEST_P(HardwareDisplayPlaneManagerAtomicTest, DisableModeset) { plane_properties_, property_names_, /*use_atomic*/ true); - EXPECT_TRUE(fake_drm_->plane_manager()->DisableModeset( - crtc_properties_[0].id, connector_properties_[0].id)); - EXPECT_EQ(1, fake_drm_->get_commit_count()); + ui::HardwareDisplayPlaneList state; + ui::CommitRequest commit_request; + commit_request.push_back(ui::CrtcCommitRequest::DisableCrtcRequest( + crtc_properties_[0].id, connector_properties_[0].id, &state)); + EXPECT_TRUE(fake_drm_->plane_manager()->Commit( + std::move(commit_request), DRM_MODE_ATOMIC_ALLOW_MODESET)); + + EXPECT_EQ(2, fake_drm_->get_commit_count()); +} + +TEST_P(HardwareDisplayPlaneManagerAtomicTest, CheckPropsAfterModeset) { + InitializeDrmState(/*crtc_count=*/1, /*planes_per_crtc=*/1); + fake_drm_->InitializeState(crtc_properties_, connector_properties_, + plane_properties_, property_names_, + /*use_atomic=*/true); + + ui::HardwareDisplayPlaneList state; + ui::CommitRequest commit_request; + ui::DrmOverlayPlaneList overlays; + overlays.push_back(ui::DrmOverlayPlane(fake_buffer_, nullptr)); + commit_request.push_back(ui::CrtcCommitRequest::EnableCrtcRequest( + crtc_properties_[0].id, connector_properties_[0].id, kDefaultMode, &state, + std::move(overlays))); + EXPECT_TRUE(fake_drm_->plane_manager()->Commit( + std::move(commit_request), DRM_MODE_ATOMIC_ALLOW_MODESET)); + + // Test props values after modesetting. + ui::DrmDevice::Property connector_prop_crtc_id; + ui::ScopedDrmObjectPropertyPtr connector_props = + fake_drm_->GetObjectProperties(kConnectorIdBase, + DRM_MODE_OBJECT_CONNECTOR); + ui::GetDrmPropertyForName(fake_drm_.get(), connector_props.get(), "CRTC_ID", + &connector_prop_crtc_id); + EXPECT_EQ(kCrtcIdPropId, connector_prop_crtc_id.id); + + ui::DrmDevice::Property crtc_prop_for_name; + ui::ScopedDrmObjectPropertyPtr crtc_props = + fake_drm_->GetObjectProperties(kCrtcIdBase, DRM_MODE_OBJECT_CRTC); + ui::GetDrmPropertyForName(fake_drm_.get(), crtc_props.get(), "ACTIVE", + &crtc_prop_for_name); + EXPECT_EQ(kActivePropId, crtc_prop_for_name.id); + EXPECT_EQ(1U, crtc_prop_for_name.value); + + ui::GetDrmPropertyForName(fake_drm_.get(), crtc_props.get(), "MODE_ID", + &crtc_prop_for_name); + EXPECT_EQ(kModePropId, crtc_prop_for_name.id); } TEST_P(HardwareDisplayPlaneManagerAtomicTest, CheckPropsAfterDisable) { @@ -475,15 +563,26 @@ TEST_P(HardwareDisplayPlaneManagerAtomicTest, CheckPropsAfterDisable) { plane_properties_, property_names_, /*use_atomic=*/true); - constexpr uint32_t kFrameBuffer = 2; ui::HardwareDisplayPlaneList state; - EXPECT_TRUE(fake_drm_->plane_manager()->Modeset( - crtc_properties_[0].id, kFrameBuffer, connector_properties_[0].id, - kDefaultMode, state)); + { + ui::CommitRequest commit_request; + ui::DrmOverlayPlaneList overlays; + overlays.push_back(ui::DrmOverlayPlane(fake_buffer_, nullptr)); + commit_request.push_back(ui::CrtcCommitRequest::EnableCrtcRequest( + crtc_properties_[0].id, connector_properties_[0].id, kDefaultMode, + &state, std::move(overlays))); + EXPECT_TRUE(fake_drm_->plane_manager()->Commit( + std::move(commit_request), DRM_MODE_ATOMIC_ALLOW_MODESET)); + } - // Test props values after disabling. - EXPECT_TRUE(fake_drm_->plane_manager()->DisableModeset( - crtc_properties_[0].id, connector_properties_[0].id)); + // Test props values after disabling. + { + ui::CommitRequest commit_request; + commit_request.push_back(ui::CrtcCommitRequest::DisableCrtcRequest( + crtc_properties_[0].id, connector_properties_[0].id, &state)); + EXPECT_TRUE(fake_drm_->plane_manager()->Commit( + std::move(commit_request), DRM_MODE_ATOMIC_ALLOW_MODESET)); + } ui::DrmDevice::Property crtc_prop_for_name; ui::ScopedDrmObjectPropertyPtr crtc_props = @@ -570,8 +669,8 @@ TEST_P(HardwareDisplayPlaneManagerAtomicTest, UnusedPlanesAreReleased) { fake_drm_->plane_manager()->BeginFrame(&hdpl); EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes( &hdpl, assigns, crtc_properties_[0].id)); - EXPECT_TRUE( - fake_drm_->plane_manager()->Commit(&hdpl, page_flip_request, nullptr)); + EXPECT_TRUE(fake_drm_->plane_manager()->Commit( + &hdpl, /*should_modeset*/ false, page_flip_request, nullptr)); assigns.clear(); assigns.push_back(ui::DrmOverlayPlane(primary_buffer, nullptr)); fake_drm_->plane_manager()->BeginFrame(&hdpl); @@ -580,12 +679,78 @@ TEST_P(HardwareDisplayPlaneManagerAtomicTest, UnusedPlanesAreReleased) { EXPECT_NE(0u, GetPlanePropertyValue(kPlaneOffset, "FB_ID")); EXPECT_NE(0u, GetPlanePropertyValue(kPlaneOffset + 1, "FB_ID")); - EXPECT_TRUE( - fake_drm_->plane_manager()->Commit(&hdpl, page_flip_request, nullptr)); + EXPECT_TRUE(fake_drm_->plane_manager()->Commit( + &hdpl, /*should_modeset*/ false, page_flip_request, nullptr)); EXPECT_NE(0u, GetPlanePropertyValue(kPlaneOffset, "FB_ID")); EXPECT_EQ(0u, GetPlanePropertyValue(kPlaneOffset + 1, "FB_ID")); } +TEST_P(HardwareDisplayPlaneManagerAtomicTest, AssignPlanesRestoresInUse) { + InitializeDrmState(/*crtc_count=*/2, /*planes_per_crtc=*/2); + fake_drm_->InitializeState(crtc_properties_, connector_properties_, + plane_properties_, property_names_, use_atomic_); + + ui::DrmOverlayPlaneList assigns; + scoped_refptr<ui::DrmFramebuffer> primary_buffer = + CreateBuffer(kDefaultBufferSize); + scoped_refptr<ui::DrmFramebuffer> overlay_buffer = + CreateBuffer(gfx::Size(1, 1)); + assigns.push_back(ui::DrmOverlayPlane(primary_buffer, nullptr)); + assigns.push_back(ui::DrmOverlayPlane(overlay_buffer, nullptr)); + ui::HardwareDisplayPlaneList hdpl; + + scoped_refptr<ui::PageFlipRequest> page_flip_request = + base::MakeRefCounted<ui::PageFlipRequest>(base::TimeDelta()); + fake_drm_->plane_manager()->BeginFrame(&hdpl); + EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes( + &hdpl, assigns, crtc_properties_[0].id)); + EXPECT_TRUE(fake_drm_->plane_manager()->Commit( + &hdpl, /*should_modeset*/ false, page_flip_request, nullptr)); + EXPECT_TRUE(fake_drm_->plane_manager()->planes().front()->in_use()); + assigns.push_back(ui::DrmOverlayPlane(overlay_buffer, nullptr)); + + fake_drm_->plane_manager()->BeginFrame(&hdpl); + // Assign overlay planes will fail since there aren't enough planes. + EXPECT_FALSE(fake_drm_->plane_manager()->AssignOverlayPlanes( + &hdpl, assigns, crtc_properties_[0].id)); + + // The primary plane should still be in use since we failed to assign + // planes and did not commit a new configuration. + EXPECT_TRUE(fake_drm_->plane_manager()->planes().front()->in_use()); +} + +TEST_P(HardwareDisplayPlaneManagerAtomicTest, PageflipTestRestoresInUse) { + InitializeDrmState(/*crtc_count=*/2, /*planes_per_crtc=*/2); + fake_drm_->InitializeState(crtc_properties_, connector_properties_, + plane_properties_, property_names_, use_atomic_); + + ui::DrmOverlayPlaneList assigns; + scoped_refptr<ui::DrmFramebuffer> primary_buffer = + CreateBuffer(kDefaultBufferSize); + scoped_refptr<ui::DrmFramebuffer> overlay_buffer = + CreateBuffer(gfx::Size(1, 1)); + assigns.push_back(ui::DrmOverlayPlane(primary_buffer, nullptr)); + assigns.push_back(ui::DrmOverlayPlane(overlay_buffer, nullptr)); + ui::HardwareDisplayPlaneList hdpl; + + scoped_refptr<ui::PageFlipRequest> page_flip_request = + base::MakeRefCounted<ui::PageFlipRequest>(base::TimeDelta()); + fake_drm_->plane_manager()->BeginFrame(&hdpl); + EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes( + &hdpl, assigns, crtc_properties_[0].id)); + EXPECT_TRUE(fake_drm_->plane_manager()->Commit( + &hdpl, /*should_modeset*/ false, page_flip_request, nullptr)); + assigns.clear(); + fake_drm_->plane_manager()->BeginFrame(&hdpl); + EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes( + &hdpl, assigns, crtc_properties_[0].id)); + EXPECT_TRUE(fake_drm_->plane_manager()->Commit( + &hdpl, /*should_modeset*/ false, nullptr, nullptr)); + // The primary plane should still be in use since the commit was + // a pageflip test and did not change any KMS state. + EXPECT_TRUE(fake_drm_->plane_manager()->planes().front()->in_use()); +} + TEST_P(HardwareDisplayPlaneManagerAtomicTest, MultipleFrames) { ui::DrmOverlayPlaneList assigns; assigns.push_back(ui::DrmOverlayPlane(fake_buffer_, nullptr)); @@ -914,8 +1079,8 @@ TEST_P(HardwareDisplayPlaneManagerAtomicTest, base::MakeRefCounted<ui::PageFlipRequest>(base::TimeDelta()); std::unique_ptr<gfx::GpuFence> out_fence; - EXPECT_TRUE(fake_drm_->plane_manager()->Commit(&state_, page_flip_request, - &out_fence)); + EXPECT_TRUE(fake_drm_->plane_manager()->Commit( + &state_, /*should_modeset*/ false, page_flip_request, &out_fence)); EXPECT_EQ(nullptr, out_fence); } @@ -1003,10 +1168,8 @@ FakeFenceFD::FakeFenceFD() { std::unique_ptr<gfx::GpuFence> FakeFenceFD::GetGpuFence() const { gfx::GpuFenceHandle handle; - handle.type = gfx::GpuFenceHandleType::kAndroidNativeFenceSync; - handle.native_fd = - base::FileDescriptor(HANDLE_EINTR(dup(read_fd.get())), true); - return std::make_unique<gfx::GpuFence>(handle); + handle.owned_fd = base::ScopedFD(HANDLE_EINTR(dup(read_fd.get()))); + return std::make_unique<gfx::GpuFence>(std::move(handle)); } void FakeFenceFD::Signal() const { @@ -1141,18 +1304,83 @@ TEST_F(HardwareDisplayPlaneManagerPlanesReadyTest, EXPECT_TRUE(callback_called); } +TEST_P(HardwareDisplayPlaneManagerAtomicTest, OverlaySourceCrop) { + InitializeDrmState(/*crtc_count=*/1, /*planes_per_crtc=*/1); + fake_drm_->InitializeState(crtc_properties_, connector_properties_, + plane_properties_, property_names_, use_atomic_); + + { + ui::DrmOverlayPlaneList assigns; + assigns.push_back(ui::DrmOverlayPlane(fake_buffer_, nullptr)); + + fake_drm_->plane_manager()->BeginFrame(&state_); + EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes( + &state_, assigns, crtc_properties_[0].id)); + + std::unique_ptr<gfx::GpuFence> out_fence; + scoped_refptr<ui::PageFlipRequest> page_flip_request = + base::MakeRefCounted<ui::PageFlipRequest>(base::TimeDelta()); + EXPECT_TRUE(fake_drm_->plane_manager()->Commit( + &state_, /*should_modeset*/ false, page_flip_request, &out_fence)); + + EXPECT_EQ(2u << 16, GetPlanePropertyValue(kPlaneOffset, "SRC_W")); + EXPECT_EQ(2u << 16, GetPlanePropertyValue(kPlaneOffset, "SRC_H")); + } + + { + ui::DrmOverlayPlaneList assigns; + assigns.push_back(ui::DrmOverlayPlane( + fake_buffer_, 0, gfx::OverlayTransform::OVERLAY_TRANSFORM_NONE, + gfx::Rect(kDefaultBufferSize), gfx::RectF(0, 0, .5, 1), false, + nullptr)); + + fake_drm_->plane_manager()->BeginFrame(&state_); + EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes( + &state_, assigns, crtc_properties_[0].id)); + + scoped_refptr<ui::PageFlipRequest> page_flip_request = + base::MakeRefCounted<ui::PageFlipRequest>(base::TimeDelta()); + std::unique_ptr<gfx::GpuFence> out_fence; + EXPECT_TRUE(fake_drm_->plane_manager()->Commit( + &state_, /*should_modeset*/ false, page_flip_request, &out_fence)); + + EXPECT_EQ(1u << 16, GetPlanePropertyValue(kPlaneOffset, "SRC_W")); + EXPECT_EQ(2u << 16, GetPlanePropertyValue(kPlaneOffset, "SRC_H")); + } + + { + ui::DrmOverlayPlaneList assigns; + assigns.push_back(ui::DrmOverlayPlane( + fake_buffer_, 0, gfx::OverlayTransform::OVERLAY_TRANSFORM_NONE, + gfx::Rect(kDefaultBufferSize), gfx::RectF(0, 0, .999, .501), false, + nullptr)); + + fake_drm_->plane_manager()->BeginFrame(&state_); + EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes( + &state_, assigns, crtc_properties_[0].id)); + + scoped_refptr<ui::PageFlipRequest> page_flip_request = + base::MakeRefCounted<ui::PageFlipRequest>(base::TimeDelta()); + std::unique_ptr<gfx::GpuFence> out_fence; + EXPECT_TRUE(fake_drm_->plane_manager()->Commit( + &state_, /*should_modeset*/ false, page_flip_request, &out_fence)); + + EXPECT_EQ(2u << 16, GetPlanePropertyValue(kPlaneOffset, "SRC_W")); + EXPECT_EQ(1u << 16, GetPlanePropertyValue(kPlaneOffset, "SRC_H")); + } +} + class HardwareDisplayPlaneAtomicMock : public ui::HardwareDisplayPlaneAtomic { public: HardwareDisplayPlaneAtomicMock() : ui::HardwareDisplayPlaneAtomic(1) {} ~HardwareDisplayPlaneAtomicMock() override = default; - bool SetPlaneData(drmModeAtomicReq* property_set, - uint32_t crtc_id, - uint32_t framebuffer, - const gfx::Rect& crtc_rect, - const gfx::Rect& src_rect, - const gfx::OverlayTransform transform, - int in_fence_fd) override { + bool AssignPlaneProps(uint32_t crtc_id, + uint32_t framebuffer, + const gfx::Rect& crtc_rect, + const gfx::Rect& src_rect, + const gfx::OverlayTransform transform, + int in_fence_fd) override { framebuffer_ = framebuffer; return true; } diff --git a/chromium/ui/ozone/platform/drm/gpu/mock_drm_device.cc b/chromium/ui/ozone/platform/drm/gpu/mock_drm_device.cc index 9463b821486..ae0984597c2 100644 --- a/chromium/ui/ozone/platform/drm/gpu/mock_drm_device.cc +++ b/chromium/ui/ozone/platform/drm/gpu/mock_drm_device.cc @@ -430,8 +430,10 @@ bool MockDrmDevice::CommitProperties( return false; for (uint32_t i = 0; i < request->cursor; ++i) { - EXPECT_TRUE(ValidatePropertyValue(request->items[i].property_id, - request->items[i].value)); + bool res = ValidatePropertyValue(request->items[i].property_id, + request->items[i].value); + if (!res) + return false; } if (page_flip_request) @@ -442,9 +444,11 @@ bool MockDrmDevice::CommitProperties( // Only update values if not testing. for (uint32_t i = 0; i < request->cursor; ++i) { - EXPECT_TRUE(UpdateProperty(request->items[i].object_id, - request->items[i].property_id, - request->items[i].value)); + bool res = + UpdateProperty(request->items[i].object_id, + request->items[i].property_id, request->items[i].value); + if (!res) + return false; } return true; diff --git a/chromium/ui/ozone/platform/drm/gpu/screen_manager.cc b/chromium/ui/ozone/platform/drm/gpu/screen_manager.cc index 342bad7004f..b7b4ebbd3ed 100644 --- a/chromium/ui/ozone/platform/drm/gpu/screen_manager.cc +++ b/chromium/ui/ozone/platform/drm/gpu/screen_manager.cc @@ -93,14 +93,63 @@ CrtcController* GetCrtcController(HardwareDisplayController* controller, return nullptr; } +inline bool AreAllStatusesTrue( + base::flat_map<int64_t, bool>& display_statuses) { + auto it = find_if(display_statuses.begin(), display_statuses.end(), + [](const auto status) { return status.second == false; }); + return (it == display_statuses.end()); +} + } // namespace -ScreenManager::ScreenManager() {} +ScreenManager::ScreenManager() = default; ScreenManager::~ScreenManager() { DCHECK(window_map_.empty()); } +ScreenManager::ControllerConfigParams::ControllerConfigParams( + int64_t display_id, + scoped_refptr<DrmDevice> drm, + uint32_t crtc, + uint32_t connector, + gfx::Point origin, + std::unique_ptr<drmModeModeInfo> pmode) + : display_id(display_id), + drm(drm), + crtc(crtc), + connector(connector), + origin(origin), + mode(std::move(pmode)) {} + +ScreenManager::ControllerConfigParams::ControllerConfigParams( + const ControllerConfigParams& other) + : display_id(other.display_id), + drm(other.drm), + crtc(other.crtc), + connector(other.connector), + origin(other.origin) { + if (other.mode) { + drmModeModeInfo mode_obj = *other.mode.get(); + mode = std::make_unique<drmModeModeInfo>(mode_obj); + } +} + +ScreenManager::ControllerConfigParams::ControllerConfigParams( + ControllerConfigParams&& other) + : display_id(other.display_id), + drm(other.drm), + crtc(other.crtc), + connector(other.connector), + origin(other.origin) { + if (other.mode) { + drmModeModeInfo mode_obj = *other.mode.get(); + mode = std::make_unique<drmModeModeInfo>(mode_obj); + } +} + +ScreenManager::ControllerConfigParams::~ControllerConfigParams() = default; + void ScreenManager::AddDisplayController(const scoped_refptr<DrmDevice>& drm, uint32_t crtc, uint32_t connector) { @@ -115,38 +164,157 @@ void ScreenManager::AddDisplayController(const scoped_refptr<DrmDevice>& drm, } controllers_.push_back(std::make_unique<HardwareDisplayController>( - std::unique_ptr<CrtcController>(new CrtcController(drm, crtc, connector)), - gfx::Point())); + std::make_unique<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(); +void ScreenManager::RemoveDisplayControllers( + const CrtcsWithDrmList& controllers_to_remove) { + // Split them to different lists unique to each DRM Device. + base::flat_map<scoped_refptr<DrmDevice>, CrtcsWithDrmList> + controllers_for_drm_devices; + for (const auto& controller : controllers_to_remove) { + auto drm = controller.second; + auto it = controllers_for_drm_devices.find(drm); + if (it == controllers_for_drm_devices.end()) { + controllers_for_drm_devices.insert( + std::make_pair(drm, CrtcsWithDrmList())); } + controllers_for_drm_devices[drm].emplace_back(controller); } + + bool should_update_controllers_to_window_mapping = false; + for (const auto& controllers_on_drm : controllers_for_drm_devices) { + CrtcsWithDrmList controllers_to_remove = controllers_on_drm.second; + + CommitRequest commit_request; + auto drm = controllers_on_drm.first; + for (const auto& controller : controllers_to_remove) { + uint32_t crtc_id = controller.first; + auto it = FindDisplayController(drm, crtc_id); + if (it == controllers_.end()) + continue; + + bool is_mirrored = (*it)->IsMirrored(); + + std::unique_ptr<CrtcController> crtc = (*it)->RemoveCrtc(drm, crtc_id); + if (!crtc->is_disabled()) { + commit_request.push_back(CrtcCommitRequest::DisableCrtcRequest( + crtc->crtc(), crtc->connector())); + } + + if (!is_mirrored) { + controllers_.erase(it); + should_update_controllers_to_window_mapping = true; + } + } + if (!commit_request.empty()) { + drm->plane_manager()->Commit(std::move(commit_request), + DRM_MODE_ATOMIC_ALLOW_MODESET); + } + } + + if (should_update_controllers_to_window_mapping) + 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) +base::flat_map<int64_t, bool> ScreenManager::ConfigureDisplayControllers( + const std::vector<ScreenManager::ControllerConfigParams>& + controllers_params) { + // Split them to different lists unique to each DRM Device. + base::flat_map<scoped_refptr<DrmDevice>, + std::vector<ScreenManager::ControllerConfigParams>> + displays_for_drms; + + for (auto& params : controllers_params) { + auto it = displays_for_drms.find(params.drm); + if (it == displays_for_drms.end()) { + displays_for_drms.insert(std::make_pair( + params.drm, std::vector<ScreenManager::ControllerConfigParams>())); + } + displays_for_drms[params.drm].emplace_back(params); + } + + base::flat_map<int64_t, bool> statuses; + // Perform display configurations together for the same DRM only. + for (const auto& configs_on_drm : displays_for_drms) { + auto display_statuses = TestAndModeset(configs_on_drm.second); + statuses.insert(display_statuses.begin(), display_statuses.end()); + } + + if (AreAllStatusesTrue(statuses)) UpdateControllerToWindowMapping(); + return statuses; +} + +base::flat_map<int64_t, bool> ScreenManager::TestAndModeset( + const std::vector<ControllerConfigParams>& controllers_params) { + if (!TestModeset(controllers_params)) { + base::flat_map<int64_t, bool> statuses; + for (const auto& params : controllers_params) + statuses.insert(std::make_pair(params.display_id, false)); + return statuses; + } + + return Modeset(controllers_params); +} + +bool ScreenManager::TestModeset( + const std::vector<ControllerConfigParams>& controllers_params) { + CommitRequest commit_request; + bool status = true; + auto drm = controllers_params[0].drm; + + for (const auto& params : controllers_params) { + auto it = FindDisplayController(params.drm, params.crtc); + DCHECK(controllers_.end() != it); + HardwareDisplayController* controller = it->get(); + + if (params.mode) { + status &= GetModesetControllerProps(&commit_request, controller, + params.origin, *params.mode); + } else { + controller->GetDisableProps(&commit_request); + } + } + if (status) { + status &= drm->plane_manager()->Commit( + std::move(commit_request), + DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_ALLOW_MODESET); + } return status; } -bool ScreenManager::ActualConfigureDisplayController( +base::flat_map<int64_t, bool> ScreenManager::Modeset( + const std::vector<ControllerConfigParams>& controllers_params) { + base::flat_map<int64_t, bool> statuses; + + for (const auto& params : controllers_params) { + // Commit one controller at a time. + CommitRequest commit_request; + bool status = params.mode + ? SetDisplayControllerForEnableAndGetProps( + &commit_request, params.drm, params.crtc, + params.connector, params.origin, *params.mode) + : SetDisableDisplayControllerForDisableAndGetProps( + &commit_request, params.drm, params.crtc); + CommitRequest request_for_update = commit_request; + if (status) { + status &= params.drm->plane_manager()->Commit( + std::move(commit_request), DRM_MODE_ATOMIC_ALLOW_MODESET); + UpdateControllerStateAfterModeset(params, request_for_update, status); + } + + statuses.insert(std::make_pair(params.display_id, status)); + } + + return statuses; +} + +// TODO(markyacoub): As Mirroring is partially handled in +// UpdateControllerStateAfterModeset(), this function can be greatly simplified. +bool ScreenManager::SetDisplayControllerForEnableAndGetProps( + CommitRequest* commit_request, const scoped_refptr<DrmDevice>& drm, uint32_t crtc, uint32_t connector, @@ -170,11 +338,11 @@ bool ScreenManager::ActualConfigureDisplayController( // 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, mode); + return HandleMirrorMode(commit_request, it, mirror, mode); } - // Just re-enable the controller to re-use the current state. - return EnableController(controller); + // Just get props to re-enable the controller re-using the current state. + return GetEnableControllerProps(commit_request, controller); } // Either the mode or the location of the display changed, so exit mirror @@ -192,12 +360,13 @@ bool ScreenManager::ActualConfigureDisplayController( FindActiveDisplayControllerByLocation(drm, modeset_bounds); // Handle mirror mode. if (mirror != controllers_.end() && it != mirror) - return HandleMirrorMode(it, mirror, drm, crtc, connector, mode); + return HandleMirrorMode(commit_request, it, mirror, mode); - return ModesetController(controller, origin, mode); + return GetModesetControllerProps(commit_request, controller, origin, mode); } -bool ScreenManager::DisableDisplayController( +bool ScreenManager::SetDisableDisplayControllerForDisableAndGetProps( + CommitRequest* commit_request, const scoped_refptr<DrmDevice>& drm, uint32_t crtc) { HardwareDisplayControllers::iterator it = FindDisplayController(drm, crtc); @@ -209,8 +378,7 @@ bool ScreenManager::DisableDisplayController( controller = controllers_.back().get(); } - controller->Disable(); - UpdateControllerToWindowMapping(); + controller->GetDisableProps(commit_request); return true; } @@ -218,6 +386,33 @@ bool ScreenManager::DisableDisplayController( return false; } +void ScreenManager::UpdateControllerStateAfterModeset( + const ControllerConfigParams& config, + const CommitRequest& commit_request, + bool did_succeed) { + for (auto& crtc_request : commit_request) { + bool was_enabled = (crtc_request.should_enable()); + + HardwareDisplayControllers::iterator it = + FindDisplayController(config.drm, crtc_request.crtc_id()); + if (it != controllers_.end()) { + it->get()->UpdateState(was_enabled, DrmOverlayPlane::GetPrimaryPlane( + crtc_request.overlays())); + + // If the CRTC is mirrored, move it to the mirror controller. + if (did_succeed && was_enabled) { + gfx::Rect modeset_bounds(config.origin, ModeSize(*config.mode)); + HardwareDisplayControllers::iterator mirror = + FindActiveDisplayControllerByLocation(config.drm, modeset_bounds); + if (mirror != controllers_.end() && it != mirror) { + (*mirror)->AddCrtc((*it)->RemoveCrtc(config.drm, config.crtc)); + controllers_.erase(it); + } + } + } + } +} + HardwareDisplayController* ScreenManager::GetDisplayController( const gfx::Rect& bounds) { HardwareDisplayControllers::iterator it = @@ -290,35 +485,18 @@ ScreenManager::FindActiveDisplayControllerByLocation( } bool ScreenManager::HandleMirrorMode( + CommitRequest* commit_request, HardwareDisplayControllers::iterator original, HardwareDisplayControllers::iterator mirror, - const scoped_refptr<DrmDevice>& drm, - uint32_t crtc, - uint32_t connector, const drmModeModeInfo& mode) { - gfx::Point last_origin = (*original)->origin(); - // There should only be one CRTC in this controller. - drmModeModeInfo last_mode = (*original)->crtc_controllers()[0]->mode(); - // Modeset the CRTC with its mode in the original controller so that only this // CRTC is affected by the mode. Otherwise it could apply a mode with the same // resolution and refresh rate but with different timings to the other CRTC. // TODO(dnicoara): This is hacky, instead the DrmDisplay and CrtcController // should be merged and picking the mode should be done properly within // HardwareDisplayController. - if (ModesetController(original->get(), (*mirror)->origin(), mode)) { - (*mirror)->AddCrtc((*original)->RemoveCrtc(drm, crtc)); - 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. - ModesetController(original->get(), last_origin, last_mode); - return false; + return GetModesetControllerProps(commit_request, original->get(), + (*mirror)->origin(), mode); } void ScreenManager::UpdateControllerToWindowMapping() { @@ -352,7 +530,10 @@ void ScreenManager::UpdateControllerToWindowMapping() { // otherwise the controller may be waiting for a page flip while the window // tries to schedule another buffer. if (should_enable) { - EnableController(controller); + CommitRequest commit_request; + GetEnableControllerProps(&commit_request, controller); + controller->GetDrmDevice()->plane_manager()->Commit( + std::move(commit_request), DRM_MODE_ATOMIC_ALLOW_MODESET); } } } @@ -412,31 +593,38 @@ DrmOverlayPlane ScreenManager::GetModesetBuffer( return DrmOverlayPlane(framebuffer, nullptr); } -bool ScreenManager::EnableController(HardwareDisplayController* controller) { +bool ScreenManager::GetEnableControllerProps( + CommitRequest* commit_request, + HardwareDisplayController* controller) { DCHECK(!controller->crtc_controllers().empty()); + gfx::Rect rect(controller->origin(), controller->GetModeSize()); - DrmOverlayPlane plane = GetModesetBuffer(controller, rect); - if (!plane.buffer || !controller->Enable(plane)) { - LOG(ERROR) << "Failed to enable controller"; + DrmOverlayPlane primary_plane = GetModesetBuffer(controller, rect); + if (!primary_plane.buffer) { + PLOG(ERROR) << "Failed to find plane buffer for Enable"; return false; } + controller->GetEnableProps(commit_request, primary_plane); return true; } -bool ScreenManager::ModesetController(HardwareDisplayController* controller, - const gfx::Point& origin, - const drmModeModeInfo& mode) { +bool ScreenManager::GetModesetControllerProps( + CommitRequest* commit_request, + 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); - DrmOverlayPlane plane = GetModesetBuffer(controller, rect); - if (!plane.buffer || !controller->Modeset(plane, mode)) { - LOG(ERROR) << "Failed to modeset controller"; + gfx::Rect rect(origin, ModeSize(mode)); + controller->set_origin(origin); + DrmOverlayPlane primary_plane = GetModesetBuffer(controller, rect); + if (!primary_plane.buffer) { + PLOG(ERROR) << "Failed to find plane buffer for Modeset"; return false; } + controller->GetModesetProps(commit_request, primary_plane, mode); return true; } diff --git a/chromium/ui/ozone/platform/drm/gpu/screen_manager.h b/chromium/ui/ozone/platform/drm/gpu/screen_manager.h index 9b1aeb2ea4a..8ed458cf1d1 100644 --- a/chromium/ui/ozone/platform/drm/gpu/screen_manager.h +++ b/chromium/ui/ozone/platform/drm/gpu/screen_manager.h @@ -9,9 +9,11 @@ #include <memory> #include <unordered_map> +#include "base/containers/flat_map.h" #include "base/macros.h" #include "base/observer_list.h" #include "ui/gfx/native_widget_types.h" +#include "ui/ozone/platform/drm/gpu/drm_display.h" #include "ui/ozone/platform/drm/gpu/hardware_display_controller.h" typedef struct _drmModeModeInfo drmModeModeInfo; @@ -29,6 +31,28 @@ class DrmWindow; // Responsible for keeping track of active displays and configuring them. class ScreenManager { public: + using CrtcsWithDrmList = + std::vector<std::pair<uint32_t, const scoped_refptr<DrmDevice>>>; + + struct ControllerConfigParams { + ControllerConfigParams(int64_t display_id, + scoped_refptr<DrmDevice> drm, + uint32_t crtc, + uint32_t connector, + gfx::Point origin, + std::unique_ptr<drmModeModeInfo> pmode); + ControllerConfigParams(const ControllerConfigParams& other); + ControllerConfigParams(ControllerConfigParams&& other); + ~ControllerConfigParams(); + + const int64_t display_id; + const scoped_refptr<DrmDevice> drm; + const uint32_t crtc; + const uint32_t connector; + const gfx::Point origin; + std::unique_ptr<drmModeModeInfo> mode = nullptr; + }; + ScreenManager(); virtual ~ScreenManager(); @@ -38,23 +62,14 @@ class ScreenManager { uint32_t crtc, uint32_t connector); - // Remove a display controller from the list of active controllers. The - // controller is removed since it was disconnected. - void RemoveDisplayController(const scoped_refptr<DrmDevice>& drm, - uint32_t crtc); - - // Configure a display controller. The display controller is identified by - // (|crtc|, |connector|) and the controller is modeset using |mode|. - bool ConfigureDisplayController(const scoped_refptr<DrmDevice>& drm, - uint32_t crtc, - uint32_t connector, - const gfx::Point& origin, - const drmModeModeInfo& mode); + // Remove display controllers from the list of active controllers. The + // controllers are removed since they were disconnected. + void RemoveDisplayControllers(const CrtcsWithDrmList& controllers_to_remove); - // Disable the display controller identified by |crtc|. Note, the controller - // may still be connected, so this does not remove the controller. - bool DisableDisplayController(const scoped_refptr<DrmDevice>& drm, - uint32_t crtc); + // Enables/Disables the display controller based on if a mode exists. + base::flat_map<int64_t, bool> ConfigureDisplayControllers( + const std::vector<ScreenManager::ControllerConfigParams>& + controllers_params); // Returns a reference to the display controller configured to display within // |bounds|. If the caller caches the controller it must also register as an @@ -90,11 +105,39 @@ class ScreenManager { const scoped_refptr<DrmDevice>& drm, uint32_t crtc); - bool ActualConfigureDisplayController(const scoped_refptr<DrmDevice>& drm, - uint32_t crtc, - uint32_t connector, - const gfx::Point& origin, - const drmModeModeInfo& mode); + base::flat_map<int64_t, bool> TestAndModeset( + const std::vector<ControllerConfigParams>& controllers_params); + + bool TestModeset( + const std::vector<ControllerConfigParams>& controllers_params); + + base::flat_map<int64_t, bool> Modeset( + const std::vector<ControllerConfigParams>& controllers_params); + + // Configures a display controller to be enabled. The display controller is + // identified by (|crtc|, |connector|) and the controller is to be modeset + // using |mode|. Controller modeset props are added into |commit_request|. + bool SetDisplayControllerForEnableAndGetProps( + CommitRequest* commit_request, + const scoped_refptr<DrmDevice>& drm, + uint32_t crtc, + uint32_t connector, + const gfx::Point& origin, + const drmModeModeInfo& mode); + + // Configures a display controller to be disabled. The display controller is + // identified by |crtc|. Controller modeset props are added into + // |commit_request|. + // Note: the controller may still be connected, so this does not remove the + // controller. + bool SetDisableDisplayControllerForDisableAndGetProps( + CommitRequest* commit_request, + const scoped_refptr<DrmDevice>& drm, + uint32_t crtc); + + void UpdateControllerStateAfterModeset(const ControllerConfigParams& config, + const CommitRequest& commit_request, + bool did_succeed); // Returns an iterator into |controllers_| for the controller located at // |origin|. @@ -107,26 +150,26 @@ class ScreenManager { const scoped_refptr<DrmDevice>& drm, const gfx::Rect& bounds); - // Tries to set the controller identified by (|crtc|, |connector|) to mirror - // those in |mirror|. |original| is an iterator to the HDC where the - // controller is currently present. - bool HandleMirrorMode(HardwareDisplayControllers::iterator original, + // Tries to set the |original| controller to mirror those in |mirror|. + // |original| is an iterator to the HDC where the controller is currently + // present. + bool HandleMirrorMode(CommitRequest* commit_request, + HardwareDisplayControllers::iterator original, HardwareDisplayControllers::iterator mirror, - const scoped_refptr<DrmDevice>& drm, - uint32_t crtc, - uint32_t connector, const drmModeModeInfo& mode); DrmOverlayPlane GetModesetBuffer(HardwareDisplayController* controller, const gfx::Rect& bounds); - bool EnableController(HardwareDisplayController* controller); - - // Modeset the |controller| using |origin| and |mode|. If there is a window at - // the controller location, then we'll re-use the current buffer. - bool ModesetController(HardwareDisplayController* controller, - const gfx::Point& origin, - const drmModeModeInfo& mode); + // Gets props for modesetting the |controller| using |origin| and |mode|. If + // there is a window at the controller location, then we'll re-use the current + // buffer. + bool GetModesetControllerProps(CommitRequest* commit_request, + HardwareDisplayController* controller, + const gfx::Point& origin, + const drmModeModeInfo& mode); + bool GetEnableControllerProps(CommitRequest* commit_request, + HardwareDisplayController* controller); DrmWindow* FindWindowAt(const gfx::Rect& bounds) const; diff --git a/chromium/ui/ozone/platform/drm/gpu/screen_manager_unittest.cc b/chromium/ui/ozone/platform/drm/gpu/screen_manager_unittest.cc index 2cbc6a6bc32..cc923828a64 100644 --- a/chromium/ui/ozone/platform/drm/gpu/screen_manager_unittest.cc +++ b/chromium/ui/ozone/platform/drm/gpu/screen_manager_unittest.cc @@ -5,6 +5,7 @@ #include <drm_fourcc.h> #include <stddef.h> #include <stdint.h> +#include <xf86drm.h> #include <memory> #include <utility> @@ -12,9 +13,11 @@ #include "base/files/platform_file.h" #include "base/macros.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/geometry/point.h" #include "ui/gfx/gpu_fence.h" #include "ui/gfx/linux/gbm_buffer.h" #include "ui/gfx/linux/test/mock_gbm_device.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_device_generator.h" #include "ui/ozone/platform/drm/gpu/drm_device_manager.h" @@ -31,12 +34,23 @@ namespace { const drmModeModeInfo kDefaultMode = {0, 6, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, {'\0'}}; -const uint32_t kPrimaryCrtc = 1; -const uint32_t kPrimaryConnector = 2; -const uint32_t kSecondaryCrtc = 3; -const uint32_t kSecondaryConnector = 4; +const uint32_t kPrimaryDisplayId = 1; +const uint32_t kSecondaryDisplayId = 2; -drmModeModeInfo Mode(uint16_t hdisplay, uint16_t vdisplay) { +constexpr uint32_t kCrtcIdBase = 100; +constexpr uint32_t kPrimaryCrtc = kCrtcIdBase; +constexpr uint32_t kSecondaryCrtc = kCrtcIdBase + 1; + +constexpr uint32_t kConnectorIdBase = 200; +constexpr uint32_t kPrimaryConnector = kConnectorIdBase; +constexpr uint32_t kSecondaryConnector = kConnectorIdBase + 1; +constexpr uint32_t kPlaneIdBase = 300; +constexpr uint32_t kInFormatsBlobPropIdBase = 400; + +constexpr uint32_t kTypePropId = 3010; +constexpr uint32_t kInFormatsPropId = 3011; + +drmModeModeInfo ConstructMode(uint16_t hdisplay, uint16_t vdisplay) { return {0, hdisplay, 0, 0, 0, 0, vdisplay, 0, 0, 0, 0, 0, 0, 0, {'\0'}}; } @@ -44,6 +58,14 @@ drmModeModeInfo Mode(uint16_t hdisplay, uint16_t vdisplay) { class ScreenManagerTest : public testing::Test { public: + struct PlaneState { + std::vector<uint32_t> formats; + }; + + struct CrtcState { + std::vector<PlaneState> planes; + }; + ScreenManagerTest() = default; ~ScreenManagerTest() override = default; @@ -57,6 +79,112 @@ class ScreenManagerTest : public testing::Test { kDefaultMode.vdisplay); } + void InitializeDrmState(ui::MockDrmDevice* drm, + const std::vector<CrtcState>& crtc_states, + bool is_atomic = true) { + std::vector<ui::MockDrmDevice::CrtcProperties> crtc_properties( + crtc_states.size()); + std::map<uint32_t, std::string> crtc_property_names = { + {1000, "ACTIVE"}, + {1001, "MODE_ID"}, + }; + + std::vector<ui::MockDrmDevice::ConnectorProperties> connector_properties(3); + std::map<uint32_t, std::string> connector_property_names = { + {2000, "CRTC_ID"}, + }; + for (size_t i = 0; i < connector_properties.size(); ++i) { + connector_properties[i].id = kPrimaryConnector + i; + for (const auto& pair : connector_property_names) { + connector_properties[i].properties.push_back( + {/* .id = */ pair.first, /* .value = */ 0}); + } + } + + std::vector<ui::MockDrmDevice::PlaneProperties> plane_properties; + std::map<uint32_t, std::string> plane_property_names = { + // Add all required properties. + {3000, "CRTC_ID"}, + {3001, "CRTC_X"}, + {3002, "CRTC_Y"}, + {3003, "CRTC_W"}, + {3004, "CRTC_H"}, + {3005, "FB_ID"}, + {3006, "SRC_X"}, + {3007, "SRC_Y"}, + {3008, "SRC_W"}, + {3009, "SRC_H"}, + // Defines some optional properties we use for convenience. + {kTypePropId, "type"}, + {kInFormatsPropId, "IN_FORMATS"}, + }; + + uint32_t plane_id = kPlaneIdBase; + uint32_t property_id = kInFormatsBlobPropIdBase; + + for (size_t crtc_idx = 0; crtc_idx < crtc_states.size(); ++crtc_idx) { + crtc_properties[crtc_idx].id = kPrimaryCrtc + crtc_idx; + for (const auto& pair : crtc_property_names) { + crtc_properties[crtc_idx].properties.push_back( + {/* .id = */ pair.first, /* .value = */ 0}); + } + + std::vector<ui::MockDrmDevice::PlaneProperties> crtc_plane_properties( + crtc_states[crtc_idx].planes.size()); + for (size_t plane_idx = 0; + plane_idx < crtc_states[crtc_idx].planes.size(); ++plane_idx) { + crtc_plane_properties[plane_idx].id = plane_id++; + crtc_plane_properties[plane_idx].crtc_mask = 1 << crtc_idx; + + for (const auto& pair : plane_property_names) { + uint64_t value = 0; + if (pair.first == kTypePropId) { + value = plane_idx == 0 ? DRM_PLANE_TYPE_PRIMARY + : DRM_PLANE_TYPE_OVERLAY; + } else if (pair.first == kInFormatsPropId) { + value = property_id++; + drm->SetPropertyBlob(ui::MockDrmDevice::AllocateInFormatsBlob( + value, crtc_states[crtc_idx].planes[plane_idx].formats, + std::vector<drm_format_modifier>())); + } + + crtc_plane_properties[plane_idx].properties.push_back( + {/* .id = */ pair.first, /* .value = */ value}); + } + } + + plane_properties.insert(plane_properties.end(), + crtc_plane_properties.begin(), + crtc_plane_properties.end()); + } + + std::map<uint32_t, std::string> property_names; + property_names.insert(crtc_property_names.begin(), + crtc_property_names.end()); + property_names.insert(connector_property_names.begin(), + connector_property_names.end()); + property_names.insert(plane_property_names.begin(), + plane_property_names.end()); + drm->InitializeState(crtc_properties, connector_properties, + plane_properties, property_names, is_atomic); + } + + void InitializeDrmStateWithDefault(ui::MockDrmDevice* drm, + bool is_atomic = true) { + // A Sample of CRTC states. + std::vector<CrtcState> crtc_states = { + {/* .planes = */ + { + {/* .formats = */ {DRM_FORMAT_XRGB8888}}, + }}, + {/* .planes = */ + { + {/* .formats = */ {DRM_FORMAT_XRGB8888}}, + }}, + }; + InitializeDrmState(drm, crtc_states, is_atomic); + } + void SetUp() override { auto gbm = std::make_unique<ui::MockGbmDevice>(); drm_ = new ui::MockDrmDevice(std::move(gbm)); @@ -100,10 +228,17 @@ TEST_F(ScreenManagerTest, CheckWithNoControllers) { } TEST_F(ScreenManagerTest, CheckWithValidController) { + InitializeDrmStateWithDefault(drm_.get()); + screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); - screen_manager_->ConfigureDisplayController( - drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(), - kDefaultMode); + + std::vector<ScreenManager::ControllerConfigParams> controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); + ui::HardwareDisplayController* controller = screen_manager_->GetDisplayController(GetPrimaryBounds()); @@ -112,51 +247,112 @@ TEST_F(ScreenManagerTest, CheckWithValidController) { } TEST_F(ScreenManagerTest, CheckWithInvalidBounds) { + InitializeDrmStateWithDefault(drm_.get()); + screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); - screen_manager_->ConfigureDisplayController( - drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(), - kDefaultMode); + + std::vector<ScreenManager::ControllerConfigParams> controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); EXPECT_TRUE(screen_manager_->GetDisplayController(GetPrimaryBounds())); EXPECT_FALSE(screen_manager_->GetDisplayController(GetSecondaryBounds())); } TEST_F(ScreenManagerTest, CheckForSecondValidController) { + InitializeDrmStateWithDefault(drm_.get()); + screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); - screen_manager_->ConfigureDisplayController( - drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(), - kDefaultMode); screen_manager_->AddDisplayController(drm_, kSecondaryCrtc, kSecondaryConnector); - screen_manager_->ConfigureDisplayController( - drm_, kSecondaryCrtc, kSecondaryConnector, GetSecondaryBounds().origin(), - kDefaultMode); + + std::vector<ScreenManager::ControllerConfigParams> controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + drmModeModeInfo secondary_mode = kDefaultMode; + controllers_to_enable.emplace_back( + kSecondaryDisplayId, drm_, kSecondaryCrtc, kSecondaryConnector, + GetSecondaryBounds().origin(), + std::make_unique<drmModeModeInfo>(secondary_mode)); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); EXPECT_TRUE(screen_manager_->GetDisplayController(GetPrimaryBounds())); EXPECT_TRUE(screen_manager_->GetDisplayController(GetSecondaryBounds())); } TEST_F(ScreenManagerTest, CheckControllerAfterItIsRemoved) { + InitializeDrmStateWithDefault(drm_.get()); + screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); - screen_manager_->ConfigureDisplayController( - drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(), - kDefaultMode); + + std::vector<ScreenManager::ControllerConfigParams> controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); + EXPECT_TRUE(screen_manager_->GetDisplayController(GetPrimaryBounds())); - screen_manager_->RemoveDisplayController(drm_, kPrimaryCrtc); + ScreenManager::CrtcsWithDrmList controllers_to_remove; + + controllers_to_remove.emplace_back(kPrimaryCrtc, drm_); + screen_manager_->RemoveDisplayControllers(controllers_to_remove); EXPECT_FALSE(screen_manager_->GetDisplayController(GetPrimaryBounds())); } +TEST_F(ScreenManagerTest, CheckMultipleControllersAfterBeingRemoved) { + InitializeDrmStateWithDefault(drm_.get()); + + screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); + screen_manager_->AddDisplayController(drm_, kSecondaryCrtc, + kSecondaryConnector); + + std::vector<ScreenManager::ControllerConfigParams> controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + controllers_to_enable.emplace_back( + kSecondaryDisplayId, drm_, kSecondaryCrtc, kSecondaryConnector, + GetSecondaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); + + ScreenManager::CrtcsWithDrmList controllers_to_remove; + controllers_to_remove.emplace_back(kPrimaryCrtc, drm_); + controllers_to_remove.emplace_back(kSecondaryCrtc, drm_); + screen_manager_->RemoveDisplayControllers(controllers_to_remove); + + EXPECT_FALSE(screen_manager_->GetDisplayController(GetPrimaryBounds())); + EXPECT_FALSE(screen_manager_->GetDisplayController(GetSecondaryBounds())); +} + TEST_F(ScreenManagerTest, CheckDuplicateConfiguration) { + InitializeDrmStateWithDefault(drm_.get(), /*is_atomic*/ false); + screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); - screen_manager_->ConfigureDisplayController( - drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(), - kDefaultMode); + + std::vector<ScreenManager::ControllerConfigParams> controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); + uint32_t framebuffer = drm_->current_framebuffer(); - screen_manager_->ConfigureDisplayController( - drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(), - kDefaultMode); + controllers_to_enable.clear(); + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); // Should not hold onto buffers. EXPECT_NE(framebuffer, drm_->current_framebuffer()); @@ -166,15 +362,30 @@ TEST_F(ScreenManagerTest, CheckDuplicateConfiguration) { } TEST_F(ScreenManagerTest, CheckChangingMode) { + InitializeDrmStateWithDefault(drm_.get()); + screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); - screen_manager_->ConfigureDisplayController( - drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(), - kDefaultMode); - drmModeModeInfo new_mode = kDefaultMode; - new_mode.vdisplay = 10; - screen_manager_->ConfigureDisplayController( - drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(), - new_mode); + + // Modeset with default mode. + { + std::vector<ScreenManager::ControllerConfigParams> controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); + } + auto new_mode = kDefaultMode; + new_mode.vdisplay = new_mode.vdisplay++; + // Modeset with a changed Mode. + { + std::vector<ScreenManager::ControllerConfigParams> controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(new_mode)); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); + } gfx::Rect new_bounds(0, 0, new_mode.hdisplay, new_mode.vdisplay); EXPECT_TRUE(screen_manager_->GetDisplayController(new_bounds)); @@ -187,49 +398,95 @@ TEST_F(ScreenManagerTest, CheckChangingMode) { } TEST_F(ScreenManagerTest, CheckForControllersInMirroredMode) { + InitializeDrmStateWithDefault(drm_.get()); + screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); - screen_manager_->ConfigureDisplayController( - drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(), - kDefaultMode); screen_manager_->AddDisplayController(drm_, kSecondaryCrtc, kSecondaryConnector); - screen_manager_->ConfigureDisplayController( - drm_, kSecondaryCrtc, kSecondaryConnector, GetPrimaryBounds().origin(), - kDefaultMode); + + std::vector<ScreenManager::ControllerConfigParams> controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + drmModeModeInfo secondary_mode = kDefaultMode; + controllers_to_enable.emplace_back( + kSecondaryDisplayId, drm_, kSecondaryCrtc, kSecondaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(secondary_mode)); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); EXPECT_TRUE(screen_manager_->GetDisplayController(GetPrimaryBounds())); EXPECT_FALSE(screen_manager_->GetDisplayController(GetSecondaryBounds())); } TEST_F(ScreenManagerTest, CheckMirrorModeTransitions) { + std::vector<CrtcState> crtc_states = { + { + /* .planes = */ + { + {/* .formats = */ {DRM_FORMAT_XRGB8888}}, + {/* .formats = */ {DRM_FORMAT_XRGB8888, DRM_FORMAT_NV12}}, + }, + }, + { + /* .planes = */ + { + {/* .formats = */ {DRM_FORMAT_XRGB8888}}, + {/* .formats = */ {DRM_FORMAT_XRGB8888, DRM_FORMAT_NV12}}, + }, + }, + }; + InitializeDrmState(drm_.get(), crtc_states); + screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); - screen_manager_->ConfigureDisplayController( - drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(), - kDefaultMode); screen_manager_->AddDisplayController(drm_, kSecondaryCrtc, kSecondaryConnector); - screen_manager_->ConfigureDisplayController( - drm_, kSecondaryCrtc, kSecondaryConnector, GetSecondaryBounds().origin(), - kDefaultMode); + + std::vector<ScreenManager::ControllerConfigParams> controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + drmModeModeInfo secondary_mode = kDefaultMode; + controllers_to_enable.emplace_back( + kSecondaryDisplayId, drm_, kSecondaryCrtc, kSecondaryConnector, + GetSecondaryBounds().origin(), + std::make_unique<drmModeModeInfo>(secondary_mode)); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); EXPECT_TRUE(screen_manager_->GetDisplayController(GetPrimaryBounds())); EXPECT_TRUE(screen_manager_->GetDisplayController(GetSecondaryBounds())); - screen_manager_->ConfigureDisplayController( - drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(), - kDefaultMode); - screen_manager_->ConfigureDisplayController( - drm_, kSecondaryCrtc, kSecondaryConnector, GetPrimaryBounds().origin(), - kDefaultMode); + controllers_to_enable.clear(); + drmModeModeInfo transition1_primary_mode = kDefaultMode; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(transition1_primary_mode)); + drmModeModeInfo transition1_secondary_mode = kDefaultMode; + controllers_to_enable.emplace_back( + kSecondaryDisplayId, drm_, kSecondaryCrtc, kSecondaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(transition1_secondary_mode)); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); + EXPECT_TRUE(screen_manager_->GetDisplayController(GetPrimaryBounds())); EXPECT_FALSE(screen_manager_->GetDisplayController(GetSecondaryBounds())); - screen_manager_->ConfigureDisplayController( - drm_, kPrimaryCrtc, kPrimaryConnector, GetSecondaryBounds().origin(), - kDefaultMode); - screen_manager_->ConfigureDisplayController( - drm_, kSecondaryCrtc, kSecondaryConnector, GetPrimaryBounds().origin(), - kDefaultMode); + controllers_to_enable.clear(); + drmModeModeInfo transition2_primary_mode = kDefaultMode; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetSecondaryBounds().origin(), + std::make_unique<drmModeModeInfo>(transition2_primary_mode)); + drmModeModeInfo transition2_secondary_mode = kDefaultMode; + controllers_to_enable.emplace_back( + kSecondaryDisplayId, drm_, kSecondaryCrtc, kSecondaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(transition2_secondary_mode)); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); + EXPECT_TRUE(screen_manager_->GetDisplayController(GetPrimaryBounds())); EXPECT_TRUE(screen_manager_->GetDisplayController(GetSecondaryBounds())); } @@ -237,22 +494,27 @@ TEST_F(ScreenManagerTest, CheckMirrorModeTransitions) { // Make sure we're using each display's mode when doing mirror mode otherwise // the timings may be off. TEST_F(ScreenManagerTest, CheckMirrorModeModesettingWithDisplaysMode) { + InitializeDrmStateWithDefault(drm_.get()); + screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); - screen_manager_->ConfigureDisplayController( - drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(), - kDefaultMode); + screen_manager_->AddDisplayController(drm_, kSecondaryCrtc, + kSecondaryConnector); + std::vector<ScreenManager::ControllerConfigParams> controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); // Copy the mode and use the copy so we can tell what mode the CRTC was // configured with. The clock value is modified so we can tell which mode is // being used. - drmModeModeInfo kSecondaryMode = kDefaultMode; - kSecondaryMode.clock++; - - screen_manager_->AddDisplayController(drm_, kSecondaryCrtc, - kSecondaryConnector); - screen_manager_->ConfigureDisplayController( - drm_, kSecondaryCrtc, kSecondaryConnector, GetPrimaryBounds().origin(), - kSecondaryMode); + drmModeModeInfo secondary_mode = kDefaultMode; + secondary_mode.clock++; + controllers_to_enable.emplace_back( + kSecondaryDisplayId, drm_, kSecondaryCrtc, kSecondaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(secondary_mode)); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); ui::HardwareDisplayController* controller = screen_manager_->GetDisplayController(GetPrimaryBounds()); @@ -260,24 +522,34 @@ TEST_F(ScreenManagerTest, CheckMirrorModeModesettingWithDisplaysMode) { if (crtc->crtc() == kPrimaryCrtc) EXPECT_EQ(kDefaultMode.clock, crtc->mode().clock); else if (crtc->crtc() == kSecondaryCrtc) - EXPECT_EQ(kSecondaryMode.clock, crtc->mode().clock); + EXPECT_EQ(secondary_mode.clock, crtc->mode().clock); else NOTREACHED(); } } TEST_F(ScreenManagerTest, MonitorGoneInMirrorMode) { + InitializeDrmStateWithDefault(drm_.get()); + screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); - screen_manager_->ConfigureDisplayController( - drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(), - kDefaultMode); screen_manager_->AddDisplayController(drm_, kSecondaryCrtc, kSecondaryConnector); - screen_manager_->ConfigureDisplayController( - drm_, kSecondaryCrtc, kSecondaryConnector, GetPrimaryBounds().origin(), - kDefaultMode); - screen_manager_->RemoveDisplayController(drm_, kSecondaryCrtc); + std::vector<ScreenManager::ControllerConfigParams> controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + drmModeModeInfo secondary_mode = kDefaultMode; + controllers_to_enable.emplace_back( + kSecondaryDisplayId, drm_, kSecondaryCrtc, kSecondaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(secondary_mode)); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); + + ScreenManager::CrtcsWithDrmList controllers_to_remove; + controllers_to_remove.emplace_back(kSecondaryCrtc, drm_); + screen_manager_->RemoveDisplayControllers(controllers_to_remove); ui::HardwareDisplayController* controller = screen_manager_->GetDisplayController(GetPrimaryBounds()); @@ -289,17 +561,26 @@ TEST_F(ScreenManagerTest, MonitorGoneInMirrorMode) { } TEST_F(ScreenManagerTest, MonitorDisabledInMirrorMode) { + InitializeDrmStateWithDefault(drm_.get()); + screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); - screen_manager_->ConfigureDisplayController( - drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(), - kDefaultMode); screen_manager_->AddDisplayController(drm_, kSecondaryCrtc, kSecondaryConnector); - screen_manager_->ConfigureDisplayController( - drm_, kSecondaryCrtc, kSecondaryConnector, GetPrimaryBounds().origin(), - kDefaultMode); - screen_manager_->DisableDisplayController(drm_, kSecondaryCrtc); + std::vector<ScreenManager::ControllerConfigParams> controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + drmModeModeInfo secondary_mode = kDefaultMode; + controllers_to_enable.emplace_back( + kSecondaryDisplayId, drm_, kSecondaryCrtc, kSecondaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(secondary_mode)); + // Disable display Controller. + controllers_to_enable.emplace_back(0, drm_, kSecondaryCrtc, 0, gfx::Point(), + nullptr); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); ui::HardwareDisplayController* controller = screen_manager_->GetDisplayController(GetPrimaryBounds()); @@ -311,87 +592,190 @@ TEST_F(ScreenManagerTest, MonitorDisabledInMirrorMode) { } TEST_F(ScreenManagerTest, DoNotEnterMirrorModeUnlessSameBounds) { + InitializeDrmStateWithDefault(drm_.get()); + screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); screen_manager_->AddDisplayController(drm_, kSecondaryCrtc, kSecondaryConnector); // Configure displays in extended mode. - screen_manager_->ConfigureDisplayController( - drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(), - kDefaultMode); - screen_manager_->ConfigureDisplayController( - drm_, kSecondaryCrtc, kSecondaryConnector, GetSecondaryBounds().origin(), - kDefaultMode); - - drmModeModeInfo new_mode = kDefaultMode; - new_mode.vdisplay = 10; - // Shouldn't enter mirror mode unless the display bounds are the same. - screen_manager_->ConfigureDisplayController( - drm_, kSecondaryCrtc, kSecondaryConnector, GetPrimaryBounds().origin(), - new_mode); + { + std::vector<ScreenManager::ControllerConfigParams> controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + drmModeModeInfo secondary_mode = kDefaultMode; + controllers_to_enable.emplace_back( + kSecondaryDisplayId, drm_, kSecondaryCrtc, kSecondaryConnector, + GetSecondaryBounds().origin(), + std::make_unique<drmModeModeInfo>(secondary_mode)); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); + } + + { + auto new_mode = std::make_unique<drmModeModeInfo>(kDefaultMode); + new_mode->vdisplay = 10; + // Shouldn't enter mirror mode unless the display bounds are the same. + std::vector<ScreenManager::ControllerConfigParams> controllers_to_enable; + controllers_to_enable.emplace_back( + kSecondaryDisplayId, drm_, kSecondaryCrtc, kSecondaryConnector, + GetPrimaryBounds().origin(), std::move(new_mode)); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); + } EXPECT_FALSE( screen_manager_->GetDisplayController(GetPrimaryBounds())->IsMirrored()); } TEST_F(ScreenManagerTest, ReuseFramebufferIfDisabledThenReEnabled) { + InitializeDrmStateWithDefault(drm_.get(), /*is_atomic=*/false); + screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); - screen_manager_->ConfigureDisplayController( - drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(), - kDefaultMode); + std::vector<ScreenManager::ControllerConfigParams> controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); + uint32_t framebuffer = drm_->current_framebuffer(); - screen_manager_->DisableDisplayController(drm_, kPrimaryCrtc); + controllers_to_enable.clear(); + // Disable display controller. + controllers_to_enable.emplace_back(0, drm_, kPrimaryCrtc, 0, gfx::Point(), + nullptr); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); EXPECT_EQ(0u, drm_->current_framebuffer()); - screen_manager_->ConfigureDisplayController( - drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(), - kDefaultMode); + controllers_to_enable.clear(); + drmModeModeInfo reenable_mode = kDefaultMode; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(reenable_mode)); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); // Buffers are released when disabled. EXPECT_NE(framebuffer, drm_->current_framebuffer()); } TEST_F(ScreenManagerTest, CheckMirrorModeAfterBeginReEnabled) { - screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); - screen_manager_->ConfigureDisplayController( - drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(), - kDefaultMode); - screen_manager_->DisableDisplayController(drm_, kPrimaryCrtc); + InitializeDrmStateWithDefault(drm_.get()); + screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); screen_manager_->AddDisplayController(drm_, kSecondaryCrtc, kSecondaryConnector); - screen_manager_->ConfigureDisplayController( - drm_, kSecondaryCrtc, kSecondaryConnector, GetPrimaryBounds().origin(), - kDefaultMode); + { + std::vector<ScreenManager::ControllerConfigParams> controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + drmModeModeInfo secondary_mode = kDefaultMode; + controllers_to_enable.emplace_back( + kSecondaryDisplayId, drm_, kSecondaryCrtc, kSecondaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(secondary_mode)); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); + } + { + std::vector<ScreenManager::ControllerConfigParams> controllers_to_enable; + controllers_to_enable.emplace_back(0, drm_, kPrimaryCrtc, 0, gfx::Point(), + nullptr); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); + } ui::HardwareDisplayController* controller = screen_manager_->GetDisplayController(GetPrimaryBounds()); EXPECT_TRUE(controller); EXPECT_FALSE(controller->IsMirrored()); - screen_manager_->ConfigureDisplayController( - drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(), - kDefaultMode); + { + std::vector<ScreenManager::ControllerConfigParams> controllers_to_enable; + drmModeModeInfo reenable_mode = kDefaultMode; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(reenable_mode)); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); + } + EXPECT_TRUE(controller); EXPECT_TRUE(controller->IsMirrored()); } +TEST_F(ScreenManagerTest, ConfigureOnDifferentDrmDevices) { + auto gbm_device = std::make_unique<ui::MockGbmDevice>(); + scoped_refptr<ui::MockDrmDevice> drm2 = + new ui::MockDrmDevice(std::move(gbm_device)); + + InitializeDrmStateWithDefault(drm_.get(), /*is_atomic=*/false); + std::vector<CrtcState> crtc_states = { + {/* .planes = */ + { + {/* .formats = */ {DRM_FORMAT_XRGB8888}}, + }}, + {/* .planes = */ + { + {/* .formats = */ {DRM_FORMAT_XRGB8888}}, + }}, + {/* .planes = */ + { + {/* .formats = */ {DRM_FORMAT_XRGB8888}}, + }}}; + InitializeDrmState(drm2.get(), crtc_states, /*is_atomic=*/false); + + screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); + screen_manager_->AddDisplayController(drm2, kSecondaryCrtc, + kSecondaryConnector); + screen_manager_->AddDisplayController(drm2, kSecondaryCrtc + 1, + kSecondaryConnector + 1); + + std::vector<ScreenManager::ControllerConfigParams> controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + drmModeModeInfo secondary_mode = kDefaultMode; + controllers_to_enable.emplace_back( + kSecondaryDisplayId, drm2, kSecondaryCrtc, kSecondaryConnector, + GetSecondaryBounds().origin(), + std::make_unique<drmModeModeInfo>(secondary_mode)); + drmModeModeInfo secondary_mode2 = kDefaultMode; + controllers_to_enable.emplace_back( + kSecondaryDisplayId + 1, drm2, kSecondaryCrtc + 1, + kSecondaryConnector + 1, GetSecondaryBounds().origin(), + std::make_unique<drmModeModeInfo>(secondary_mode2)); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); + + EXPECT_EQ(drm_->get_set_crtc_call_count(), 1); + EXPECT_EQ(drm2->get_set_crtc_call_count(), 2); +} + TEST_F(ScreenManagerTest, CheckProperConfigurationWithDifferentDeviceAndSameCrtc) { auto gbm_device = std::make_unique<ui::MockGbmDevice>(); scoped_refptr<ui::MockDrmDevice> drm2 = new ui::MockDrmDevice(std::move(gbm_device)); + InitializeDrmStateWithDefault(drm_.get()); + InitializeDrmStateWithDefault(drm2.get()); + screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); screen_manager_->AddDisplayController(drm2, kPrimaryCrtc, kPrimaryConnector); - screen_manager_->ConfigureDisplayController( - drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(), - kDefaultMode); - screen_manager_->ConfigureDisplayController( - drm2, kPrimaryCrtc, kPrimaryConnector, GetSecondaryBounds().origin(), - kDefaultMode); + std::vector<ScreenManager::ControllerConfigParams> controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + drmModeModeInfo secondary_mode = kDefaultMode; + controllers_to_enable.emplace_back( + kSecondaryDisplayId, drm2, kPrimaryCrtc, kPrimaryConnector, + GetSecondaryBounds().origin(), + std::make_unique<drmModeModeInfo>(secondary_mode)); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); ui::HardwareDisplayController* controller1 = screen_manager_->GetDisplayController(GetPrimaryBounds()); @@ -404,6 +788,8 @@ TEST_F(ScreenManagerTest, } TEST_F(ScreenManagerTest, CheckControllerToWindowMappingWithSameBounds) { + InitializeDrmStateWithDefault(drm_.get()); + std::unique_ptr<ui::DrmWindow> window( new ui::DrmWindow(1, device_manager_.get(), screen_manager_.get())); window->Initialize(); @@ -411,9 +797,12 @@ TEST_F(ScreenManagerTest, CheckControllerToWindowMappingWithSameBounds) { screen_manager_->AddWindow(1, std::move(window)); screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); - screen_manager_->ConfigureDisplayController( - drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(), - kDefaultMode); + std::vector<ScreenManager::ControllerConfigParams> controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); EXPECT_TRUE(screen_manager_->GetWindow(1)->GetController()); @@ -422,6 +811,8 @@ TEST_F(ScreenManagerTest, CheckControllerToWindowMappingWithSameBounds) { } TEST_F(ScreenManagerTest, CheckControllerToWindowMappingWithDifferentBounds) { + InitializeDrmStateWithDefault(drm_.get()); + std::unique_ptr<ui::DrmWindow> window( new ui::DrmWindow(1, device_manager_.get(), screen_manager_.get())); window->Initialize(); @@ -431,9 +822,12 @@ TEST_F(ScreenManagerTest, CheckControllerToWindowMappingWithDifferentBounds) { screen_manager_->AddWindow(1, std::move(window)); screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); - screen_manager_->ConfigureDisplayController( - drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(), - kDefaultMode); + std::vector<ScreenManager::ControllerConfigParams> controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); EXPECT_FALSE(screen_manager_->GetWindow(1)->GetController()); @@ -443,6 +837,8 @@ TEST_F(ScreenManagerTest, CheckControllerToWindowMappingWithDifferentBounds) { TEST_F(ScreenManagerTest, CheckControllerToWindowMappingWithOverlappingWindows) { + InitializeDrmStateWithDefault(drm_.get()); + const size_t kWindowCount = 2; for (size_t i = 1; i < kWindowCount + 1; ++i) { std::unique_ptr<ui::DrmWindow> window( @@ -453,9 +849,12 @@ TEST_F(ScreenManagerTest, } screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); - screen_manager_->ConfigureDisplayController( - drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(), - kDefaultMode); + std::vector<ScreenManager::ControllerConfigParams> controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); bool window1_has_controller = screen_manager_->GetWindow(1)->GetController(); bool window2_has_controller = screen_manager_->GetWindow(2)->GetController(); @@ -469,6 +868,8 @@ TEST_F(ScreenManagerTest, } TEST_F(ScreenManagerTest, ShouldDissociateWindowOnControllerRemoval) { + InitializeDrmStateWithDefault(drm_.get()); + gfx::AcceleratedWidget window_id = 1; std::unique_ptr<ui::DrmWindow> window(new ui::DrmWindow( window_id, device_manager_.get(), screen_manager_.get())); @@ -477,13 +878,18 @@ TEST_F(ScreenManagerTest, ShouldDissociateWindowOnControllerRemoval) { screen_manager_->AddWindow(window_id, std::move(window)); screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); - screen_manager_->ConfigureDisplayController( - drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(), - kDefaultMode); + std::vector<ScreenManager::ControllerConfigParams> controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); EXPECT_TRUE(screen_manager_->GetWindow(window_id)->GetController()); - screen_manager_->RemoveDisplayController(drm_, kPrimaryCrtc); + ScreenManager::CrtcsWithDrmList controllers_to_remove; + controllers_to_remove.emplace_back(kPrimaryCrtc, drm_); + screen_manager_->RemoveDisplayControllers(controllers_to_remove); EXPECT_FALSE(screen_manager_->GetWindow(window_id)->GetController()); @@ -492,6 +898,8 @@ TEST_F(ScreenManagerTest, ShouldDissociateWindowOnControllerRemoval) { } TEST_F(ScreenManagerTest, EnableControllerWhenWindowHasNoBuffer) { + InitializeDrmStateWithDefault(drm_.get(), /*is_atomic=*/false); + std::unique_ptr<ui::DrmWindow> window( new ui::DrmWindow(1, device_manager_.get(), screen_manager_.get())); window->Initialize(); @@ -499,18 +907,24 @@ TEST_F(ScreenManagerTest, EnableControllerWhenWindowHasNoBuffer) { screen_manager_->AddWindow(1, std::move(window)); screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); - screen_manager_->ConfigureDisplayController( - drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(), - kDefaultMode); + std::vector<ScreenManager::ControllerConfigParams> controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); EXPECT_TRUE(screen_manager_->GetWindow(1)->GetController()); // There is a buffer after initial config. uint32_t framebuffer = drm_->current_framebuffer(); EXPECT_NE(0U, framebuffer); - screen_manager_->ConfigureDisplayController( - drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(), - kDefaultMode); + controllers_to_enable.clear(); + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); // There is a new buffer after we configured with the same mode but no // pending frames on the window. @@ -521,6 +935,8 @@ TEST_F(ScreenManagerTest, EnableControllerWhenWindowHasNoBuffer) { } TEST_F(ScreenManagerTest, EnableControllerWhenWindowHasBuffer) { + InitializeDrmStateWithDefault(drm_.get(), /*is_atomic=*/false); + std::unique_ptr<ui::DrmWindow> window( new ui::DrmWindow(1, device_manager_.get(), screen_manager_.get())); window->Initialize(); @@ -534,9 +950,12 @@ TEST_F(ScreenManagerTest, EnableControllerWhenWindowHasBuffer) { screen_manager_->AddWindow(1, std::move(window)); screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); - screen_manager_->ConfigureDisplayController( - drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(), - kDefaultMode); + std::vector<ScreenManager::ControllerConfigParams> controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); EXPECT_EQ(buffer->opaque_framebuffer_id(), drm_->current_framebuffer()); @@ -559,9 +978,12 @@ TEST_F(ScreenManagerTest, DISABLED_RejectBufferWithIncompatibleModifiers) { screen_manager_->AddWindow(1, std::move(window)); screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); - screen_manager_->ConfigureDisplayController( - drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(), - kDefaultMode); + std::vector<ScreenManager::ControllerConfigParams> controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); // ScreenManager::GetModesetBuffer (called to get a buffer to // modeset the new controller) should reject the buffer with @@ -575,6 +997,8 @@ TEST_F(ScreenManagerTest, DISABLED_RejectBufferWithIncompatibleModifiers) { } TEST_F(ScreenManagerTest, ConfigureDisplayControllerShouldModesetOnce) { + InitializeDrmStateWithDefault(drm_.get(), /*is_atomic=*/false); + std::unique_ptr<ui::DrmWindow> window( new ui::DrmWindow(1, device_manager_.get(), screen_manager_.get())); window->Initialize(); @@ -582,9 +1006,12 @@ TEST_F(ScreenManagerTest, ConfigureDisplayControllerShouldModesetOnce) { screen_manager_->AddWindow(1, std::move(window)); screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); - screen_manager_->ConfigureDisplayController( - drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(), - kDefaultMode); + std::vector<ScreenManager::ControllerConfigParams> controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); // When a window that had no controller becomes associated with a new // controller, expect the crtc to be modeset once. @@ -594,28 +1021,38 @@ TEST_F(ScreenManagerTest, ConfigureDisplayControllerShouldModesetOnce) { window->Shutdown(); } -TEST(ScreenManagerTest2, ShouldNotHardwareMirrorDifferentDrmDevices) { +TEST_F(ScreenManagerTest, ShouldNotHardwareMirrorDifferentDrmDevices) { auto gbm_device1 = std::make_unique<MockGbmDevice>(); auto drm_device1 = base::MakeRefCounted<MockDrmDevice>(std::move(gbm_device1)); + InitializeDrmStateWithDefault(drm_device1.get()); + auto gbm_device2 = std::make_unique<MockGbmDevice>(); auto drm_device2 = base::MakeRefCounted<MockDrmDevice>(std::move(gbm_device2)); + InitializeDrmStateWithDefault(drm_device2.get()); + DrmDeviceManager drm_device_manager(nullptr); ScreenManager screen_manager; - constexpr uint32_t kCrtc19 = 19; - constexpr uint32_t kConnector28 = 28; - constexpr uint32_t kCrtc20 = 20; - constexpr uint32_t kConnector22 = 22; + constexpr uint32_t kCrtc1 = kPrimaryCrtc; + constexpr uint32_t kConnector1 = kPrimaryConnector; + constexpr uint32_t kCrtc2 = kSecondaryCrtc; + constexpr uint32_t kConnector2 = kSecondaryConnector; + + drmModeModeInfo k1920x1080Screen = ConstructMode(1920, 1080); + std::unique_ptr<drmModeModeInfo> primary_mode = + std::make_unique<drmModeModeInfo>(k1920x1080Screen); + std::unique_ptr<drmModeModeInfo> secondary_mode = + std::make_unique<drmModeModeInfo>(k1920x1080Screen); // Two displays on different DRM devices must not join a mirror pair. // // However, they may have the same bounds in a transitional state. // - // This scenario generates the same sequence of display configuration events - // as a panther (kernel 3.8.11) chromebox with two identical 1080p displays - // connected, one of them via a DisplayLink adapter. + // This scenario generates the same sequence of display configuration + // events as a panther (kernel 3.8.11) chromebox with two identical + // 1080p displays connected, one of them via a DisplayLink adapter. // Both displays connect at startup. { @@ -624,13 +1061,22 @@ TEST(ScreenManagerTest2, ShouldNotHardwareMirrorDifferentDrmDevices) { window1->Initialize(); screen_manager.AddWindow(1, std::move(window1)); screen_manager.GetWindow(1)->SetBounds(gfx::Rect(0, 0, 1920, 1080)); - screen_manager.AddDisplayController(drm_device1, kCrtc19, kConnector28); - screen_manager.AddDisplayController(drm_device2, kCrtc20, kConnector22); - screen_manager.ConfigureDisplayController( - drm_device1, kCrtc19, kConnector28, gfx::Point(0, 0), Mode(1920, 1080)); - screen_manager.ConfigureDisplayController(drm_device2, kCrtc20, - kConnector22, gfx::Point(0, 1140), - Mode(1920, 1080)); + screen_manager.AddDisplayController(drm_device1, kCrtc1, kConnector1); + screen_manager.AddDisplayController(drm_device2, kCrtc2, kConnector2); + + std::vector<ScreenManager::ControllerConfigParams> controllers_to_enable; + std::unique_ptr<drmModeModeInfo> primary_mode = + std::make_unique<drmModeModeInfo>(k1920x1080Screen); + std::unique_ptr<drmModeModeInfo> secondary_mode = + std::make_unique<drmModeModeInfo>(k1920x1080Screen); + controllers_to_enable.emplace_back(kPrimaryDisplayId, drm_device1, kCrtc1, + kConnector1, gfx::Point(0, 0), + std::move(primary_mode)); + controllers_to_enable.emplace_back(kSecondaryDisplayId, drm_device2, kCrtc2, + kConnector2, gfx::Point(0, 1140), + std::move(secondary_mode)); + screen_manager.ConfigureDisplayControllers(controllers_to_enable); + auto window2 = std::make_unique<DrmWindow>(2, &drm_device_manager, &screen_manager); window2->Initialize(); @@ -645,15 +1091,24 @@ TEST(ScreenManagerTest2, ShouldNotHardwareMirrorDifferentDrmDevices) { HardwareDisplayController* controller2 = screen_manager.GetWindow(2)->GetController(); EXPECT_NE(controller1, controller2); - EXPECT_TRUE(controller1->HasCrtc(drm_device1, kCrtc19)); - EXPECT_TRUE(controller2->HasCrtc(drm_device2, kCrtc20)); + EXPECT_TRUE(controller1->HasCrtc(drm_device1, kCrtc1)); + EXPECT_TRUE(controller2->HasCrtc(drm_device2, kCrtc2)); } // Disconnect first display. Second display moves to origin. { - screen_manager.RemoveDisplayController(drm_device1, kCrtc19); - screen_manager.ConfigureDisplayController( - drm_device2, kCrtc20, kConnector22, gfx::Point(0, 0), Mode(1920, 1080)); + ScreenManager::CrtcsWithDrmList controllers_to_remove; + controllers_to_remove.emplace_back(kCrtc1, drm_device1); + screen_manager.RemoveDisplayControllers(controllers_to_remove); + + std::vector<ScreenManager::ControllerConfigParams> controllers_to_enable; + std::unique_ptr<drmModeModeInfo> secondary_mode = + std::make_unique<drmModeModeInfo>(k1920x1080Screen); + controllers_to_enable.emplace_back(kSecondaryDisplayId, drm_device2, kCrtc2, + kConnector2, gfx::Point(0, 0), + std::move(secondary_mode)); + screen_manager.ConfigureDisplayControllers(controllers_to_enable); + screen_manager.GetWindow(1)->SetBounds(gfx::Rect(0, 0, 1920, 1080)); screen_manager.GetWindow(1)->SetBounds(gfx::Rect(0, 0, 1920, 1080)); screen_manager.RemoveWindow(2)->Shutdown(); @@ -661,22 +1116,31 @@ TEST(ScreenManagerTest2, ShouldNotHardwareMirrorDifferentDrmDevices) { // Reconnect first display. Original configuration restored. { - screen_manager.AddDisplayController(drm_device1, kCrtc19, kConnector28); - screen_manager.ConfigureDisplayController( - drm_device1, kCrtc19, kConnector28, gfx::Point(0, 0), Mode(1920, 1080)); + screen_manager.AddDisplayController(drm_device1, kCrtc1, kConnector1); + std::vector<ScreenManager::ControllerConfigParams> controllers_to_enable; + std::unique_ptr<drmModeModeInfo> primary_mode = + std::make_unique<drmModeModeInfo>(k1920x1080Screen); + controllers_to_enable.emplace_back(kPrimaryDisplayId, drm_device1, kCrtc1, + kConnector1, gfx::Point(0, 0), + std::move(primary_mode)); + screen_manager.ConfigureDisplayControllers(controllers_to_enable); // At this point, both displays are in the same location. { HardwareDisplayController* controller = screen_manager.GetWindow(1)->GetController(); EXPECT_FALSE(controller->IsMirrored()); - // We don't really care which crtc it has, but it should have just one. + // We don't really care which crtc it has, but it should have just EXPECT_EQ(1U, controller->crtc_controllers().size()); - EXPECT_TRUE(controller->HasCrtc(drm_device1, kCrtc19) || - controller->HasCrtc(drm_device2, kCrtc20)); + EXPECT_TRUE(controller->HasCrtc(drm_device1, kCrtc1) || + controller->HasCrtc(drm_device2, kCrtc2)); } - screen_manager.ConfigureDisplayController(drm_device2, kCrtc20, - kConnector22, gfx::Point(0, 1140), - Mode(1920, 1080)); + controllers_to_enable.clear(); + std::unique_ptr<drmModeModeInfo> secondary_mode = + std::make_unique<drmModeModeInfo>(k1920x1080Screen); + controllers_to_enable.emplace_back(kSecondaryDisplayId, drm_device2, kCrtc2, + kConnector2, gfx::Point(0, 1140), + std::move(secondary_mode)); + screen_manager.ConfigureDisplayControllers(controllers_to_enable); auto window3 = std::make_unique<DrmWindow>(3, &drm_device_manager, &screen_manager); window3->Initialize(); @@ -694,8 +1158,8 @@ TEST(ScreenManagerTest2, ShouldNotHardwareMirrorDifferentDrmDevices) { HardwareDisplayController* controller3 = screen_manager.GetWindow(3)->GetController(); EXPECT_NE(controller1, controller3); - EXPECT_TRUE(controller1->HasCrtc(drm_device1, kCrtc19)); - EXPECT_TRUE(controller3->HasCrtc(drm_device2, kCrtc20)); + EXPECT_TRUE(controller1->HasCrtc(drm_device1, kCrtc1)); + EXPECT_TRUE(controller3->HasCrtc(drm_device2, kCrtc2)); } // Cleanup. @@ -704,18 +1168,20 @@ TEST(ScreenManagerTest2, ShouldNotHardwareMirrorDifferentDrmDevices) { } // crbug.com/888553 -TEST(ScreenManagerTest2, ShouldNotUnbindFramebufferOnJoiningMirror) { +TEST_F(ScreenManagerTest, ShouldNotUnbindFramebufferOnJoiningMirror) { auto gbm_device = std::make_unique<MockGbmDevice>(); auto drm_device = base::MakeRefCounted<MockDrmDevice>(std::move(gbm_device)); + InitializeDrmStateWithDefault(drm_device.get(), /*is_atomic=*/false); + DrmDeviceManager drm_device_manager(nullptr); ScreenManager screen_manager; - constexpr uint32_t kCrtc39 = 39; - constexpr uint32_t kConnector43 = 43; - constexpr uint32_t kCrtc41 = 41; - constexpr uint32_t kConnector46 = 46; + constexpr uint32_t kCrtc1 = kPrimaryCrtc; + constexpr uint32_t kConnector1 = kPrimaryConnector; + constexpr uint32_t kCrtc2 = kSecondaryCrtc; + constexpr uint32_t kConnector2 = kSecondaryConnector; - constexpr drmModeModeInfo kMode1080p60 = { + constexpr drmModeModeInfo k1080p60Screen = { /* clock= */ 148500, /* hdisplay= */ 1920, /* hsync_start= */ 2008, @@ -740,16 +1206,25 @@ TEST(ScreenManagerTest2, ShouldNotUnbindFramebufferOnJoiningMirror) { window1->Initialize(); screen_manager.AddWindow(1, std::move(window1)); screen_manager.GetWindow(1)->SetBounds(gfx::Rect(0, 0, 1920, 1080)); - screen_manager.AddDisplayController(drm_device, kCrtc39, kConnector43); - screen_manager.AddDisplayController(drm_device, kCrtc41, kConnector46); - screen_manager.ConfigureDisplayController(drm_device, kCrtc39, kConnector43, - gfx::Point(0, 0), kMode1080p60); - screen_manager.ConfigureDisplayController(drm_device, kCrtc41, kConnector46, - gfx::Point(0, 0), kMode1080p60); + screen_manager.AddDisplayController(drm_device, kCrtc1, kConnector1); + screen_manager.AddDisplayController(drm_device, kCrtc2, kConnector2); + + std::vector<ScreenManager::ControllerConfigParams> controllers_to_enable; + std::unique_ptr<drmModeModeInfo> primary_mode = + std::make_unique<drmModeModeInfo>(k1080p60Screen); + std::unique_ptr<drmModeModeInfo> secondary_mode = + std::make_unique<drmModeModeInfo>(k1080p60Screen); + controllers_to_enable.emplace_back(kPrimaryDisplayId, drm_device, kCrtc1, + kConnector1, gfx::Point(0, 0), + std::move(primary_mode)); + controllers_to_enable.emplace_back(kSecondaryDisplayId, drm_device, kCrtc2, + kConnector2, gfx::Point(0, 0), + std::move(secondary_mode)); + screen_manager.ConfigureDisplayControllers(controllers_to_enable); } - EXPECT_NE(0u, drm_device->GetFramebufferForCrtc(kCrtc39)); - EXPECT_NE(0u, drm_device->GetFramebufferForCrtc(kCrtc41)); + EXPECT_NE(0u, drm_device->GetFramebufferForCrtc(kCrtc1)); + EXPECT_NE(0u, drm_device->GetFramebufferForCrtc(kCrtc2)); // Cleanup. screen_manager.RemoveWindow(1)->Shutdown(); diff --git a/chromium/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.cc b/chromium/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.cc index e3792ee932d..fa3d41f7afd 100644 --- a/chromium/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.cc +++ b/chromium/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.cc @@ -134,10 +134,8 @@ std::unique_ptr<gfx::GpuFence> VulkanImplementationGbm::ExportVkFenceToGpuFence( } gfx::GpuFenceHandle gpu_fence_handle; - gpu_fence_handle.type = gfx::GpuFenceHandleType::kAndroidNativeFenceSync; - gpu_fence_handle.native_fd = - base::FileDescriptor(fence_fd, true /* auto_close */); - return std::make_unique<gfx::GpuFence>(gpu_fence_handle); + gpu_fence_handle.owned_fd = base::ScopedFD(fence_fd); + return std::make_unique<gfx::GpuFence>(std::move(gpu_fence_handle)); } VkSemaphore VulkanImplementationGbm::CreateExternalSemaphore( diff --git a/chromium/ui/ozone/platform/drm/host/drm_display_host.cc b/chromium/ui/ozone/platform/drm/host/drm_display_host.cc index 23c44ce2e5a..f46106b9c0c 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_display_host.cc +++ b/chromium/ui/ozone/platform/drm/host/drm_display_host.cc @@ -38,15 +38,18 @@ void DrmDisplayHost::UpdateDisplaySnapshot( void DrmDisplayHost::GetHDCPState(display::GetHDCPStateCallback callback) { get_hdcp_callback_ = std::move(callback); if (!sender_->GpuGetHDCPState(snapshot_->display_id())) - OnHDCPStateReceived(false, display::HDCP_STATE_UNDESIRED); + OnHDCPStateReceived(false, display::HDCP_STATE_UNDESIRED, + display::CONTENT_PROTECTION_METHOD_NONE); } -void DrmDisplayHost::OnHDCPStateReceived(bool status, - display::HDCPState state) { +void DrmDisplayHost::OnHDCPStateReceived( + bool status, + display::HDCPState state, + display::ContentProtectionMethod protection_method) { if (!get_hdcp_callback_.is_null()) { base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindOnce(std::move(get_hdcp_callback_), status, state)); + FROM_HERE, base::BindOnce(std::move(get_hdcp_callback_), status, state, + protection_method)); } else { LOG(ERROR) << "Got unexpected event for display " << snapshot_->display_id(); @@ -55,10 +58,13 @@ void DrmDisplayHost::OnHDCPStateReceived(bool status, get_hdcp_callback_.Reset(); } -void DrmDisplayHost::SetHDCPState(display::HDCPState state, - display::SetHDCPStateCallback callback) { +void DrmDisplayHost::SetHDCPState( + display::HDCPState state, + display::ContentProtectionMethod protection_method, + display::SetHDCPStateCallback callback) { set_hdcp_callback_ = std::move(callback); - if (!sender_->GpuSetHDCPState(snapshot_->display_id(), state)) + if (!sender_->GpuSetHDCPState(snapshot_->display_id(), state, + protection_method)) OnHDCPStateUpdated(false); } @@ -103,7 +109,8 @@ void DrmDisplayHost::OnGpuThreadRetired() {} void DrmDisplayHost::ClearCallbacks() { if (!get_hdcp_callback_.is_null()) - OnHDCPStateReceived(false, display::HDCP_STATE_UNDESIRED); + OnHDCPStateReceived(false, display::HDCP_STATE_UNDESIRED, + display::CONTENT_PROTECTION_METHOD_NONE); if (!set_hdcp_callback_.is_null()) OnHDCPStateUpdated(false); } diff --git a/chromium/ui/ozone/platform/drm/host/drm_display_host.h b/chromium/ui/ozone/platform/drm/host/drm_display_host.h index 5bb6a114ee3..d94fb6d5461 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_display_host.h +++ b/chromium/ui/ozone/platform/drm/host/drm_display_host.h @@ -34,6 +34,7 @@ class DrmDisplayHost : public GpuThreadObserver { void UpdateDisplaySnapshot(std::unique_ptr<display::DisplaySnapshot> params); void GetHDCPState(display::GetHDCPStateCallback callback); void SetHDCPState(display::HDCPState state, + display::ContentProtectionMethod protection_method, display::SetHDCPStateCallback callback); void SetColorMatrix(const std::vector<float>& color_matrix); void SetGammaCorrection( @@ -43,7 +44,9 @@ class DrmDisplayHost : public GpuThreadObserver { // Called when the IPC from the GPU process arrives to answer the above // commands. - void OnHDCPStateReceived(bool status, display::HDCPState state); + void OnHDCPStateReceived(bool status, + display::HDCPState state, + display::ContentProtectionMethod protection_method); void OnHDCPStateUpdated(bool status); // GpuThreadObserver: diff --git a/chromium/ui/ozone/platform/drm/host/drm_display_host_manager.cc b/chromium/ui/ozone/platform/drm/host/drm_display_host_manager.cc index e3429cbce13..975da5c6ab3 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_display_host_manager.cc +++ b/chromium/ui/ozone/platform/drm/host/drm_display_host_manager.cc @@ -412,12 +412,14 @@ void DrmDisplayHostManager::GpuHasUpdatedNativeDisplays( } } -void DrmDisplayHostManager::GpuReceivedHDCPState(int64_t display_id, - bool status, - display::HDCPState state) { +void DrmDisplayHostManager::GpuReceivedHDCPState( + int64_t display_id, + bool status, + display::HDCPState state, + display::ContentProtectionMethod protection_method) { DrmDisplayHost* display = GetDisplay(display_id); if (display) - display->OnHDCPStateReceived(status, state); + display->OnHDCPStateReceived(status, state, protection_method); else LOG(ERROR) << "Couldn't find display with id=" << display_id; } diff --git a/chromium/ui/ozone/platform/drm/host/drm_display_host_manager.h b/chromium/ui/ozone/platform/drm/host/drm_display_host_manager.h index bddc9b5beac..96279fd74af 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_display_host_manager.h +++ b/chromium/ui/ozone/platform/drm/host/drm_display_host_manager.h @@ -68,7 +68,8 @@ class DrmDisplayHostManager : public DeviceEventObserver, GpuThreadObserver { void GpuHasUpdatedNativeDisplays(MovableDisplaySnapshots displays); void GpuReceivedHDCPState(int64_t display_id, bool status, - display::HDCPState state); + display::HDCPState state, + display::ContentProtectionMethod protection_method); void GpuUpdatedHDCPState(int64_t display_id, bool status); void GpuTookDisplayControl(bool status); void GpuRelinquishedDisplayControl(bool status); diff --git a/chromium/ui/ozone/platform/drm/host/drm_native_display_delegate.cc b/chromium/ui/ozone/platform/drm/host/drm_native_display_delegate.cc index c239f99e51d..4c127b24b5f 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_native_display_delegate.cc +++ b/chromium/ui/ozone/platform/drm/host/drm_native_display_delegate.cc @@ -66,9 +66,10 @@ void DrmNativeDisplayDelegate::GetHDCPState( void DrmNativeDisplayDelegate::SetHDCPState( const display::DisplaySnapshot& output, display::HDCPState state, + display::ContentProtectionMethod protection_method, display::SetHDCPStateCallback callback) { DrmDisplayHost* display = display_manager_->GetDisplay(output.display_id()); - display->SetHDCPState(state, std::move(callback)); + display->SetHDCPState(state, protection_method, std::move(callback)); } bool DrmNativeDisplayDelegate::SetColorMatrix( diff --git a/chromium/ui/ozone/platform/drm/host/drm_native_display_delegate.h b/chromium/ui/ozone/platform/drm/host/drm_native_display_delegate.h index fd141f1fdc6..b2ee384fc3c 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_native_display_delegate.h +++ b/chromium/ui/ozone/platform/drm/host/drm_native_display_delegate.h @@ -36,6 +36,7 @@ class DrmNativeDisplayDelegate : public display::NativeDisplayDelegate { display::GetHDCPStateCallback callback) override; void SetHDCPState(const display::DisplaySnapshot& output, display::HDCPState state, + display::ContentProtectionMethod protection_method, display::SetHDCPStateCallback callback) override; bool SetColorMatrix(int64_t display_id, const std::vector<float>& color_matrix) override; diff --git a/chromium/ui/ozone/platform/drm/host/gpu_thread_adapter.h b/chromium/ui/ozone/platform/drm/host/gpu_thread_adapter.h index c782923ddf1..aa64e27b463 100644 --- a/chromium/ui/ozone/platform/drm/host/gpu_thread_adapter.h +++ b/chromium/ui/ozone/platform/drm/host/gpu_thread_adapter.h @@ -48,8 +48,10 @@ class GpuThreadAdapter { const std::vector<display::DisplayConfigurationParams>& config_requests, display::ConfigureCallback callback) = 0; virtual bool GpuGetHDCPState(int64_t display_id) = 0; - virtual bool GpuSetHDCPState(int64_t display_id, - display::HDCPState state) = 0; + virtual bool GpuSetHDCPState( + int64_t display_id, + display::HDCPState state, + display::ContentProtectionMethod protection_method) = 0; virtual bool GpuSetColorMatrix(int64_t display_id, const std::vector<float>& color_matrix) = 0; virtual bool GpuSetGammaCorrection( diff --git a/chromium/ui/ozone/platform/drm/host/host_drm_device.cc b/chromium/ui/ozone/platform/drm/host/host_drm_device.cc index 960df0d3905..627db7ce4f8 100644 --- a/chromium/ui/ozone/platform/drm/host/host_drm_device.cc +++ b/chromium/ui/ozone/platform/drm/host/host_drm_device.cc @@ -208,14 +208,17 @@ bool HostDrmDevice::GpuGetHDCPState(int64_t display_id) { return true; } -bool HostDrmDevice::GpuSetHDCPState(int64_t display_id, - display::HDCPState state) { +bool HostDrmDevice::GpuSetHDCPState( + int64_t display_id, + display::HDCPState state, + display::ContentProtectionMethod protection_method) { DCHECK_CALLED_ON_VALID_THREAD(on_ui_thread_); if (!IsConnected()) return false; auto callback = base::BindOnce(&HostDrmDevice::GpuSetHDCPStateCallback, this); - drm_device_->SetHDCPState(display_id, state, std::move(callback)); + drm_device_->SetHDCPState(display_id, state, protection_method, + std::move(callback)); return true; } @@ -267,11 +270,14 @@ void HostDrmDevice::GpuRelinquishDisplayControlCallback(bool success) const { display_manager_->GpuRelinquishedDisplayControl(success); } -void HostDrmDevice::GpuGetHDCPStateCallback(int64_t display_id, - bool success, - display::HDCPState state) const { +void HostDrmDevice::GpuGetHDCPStateCallback( + int64_t display_id, + bool success, + display::HDCPState state, + display::ContentProtectionMethod protection_method) const { DCHECK_CALLED_ON_VALID_THREAD(on_ui_thread_); - display_manager_->GpuReceivedHDCPState(display_id, success, state); + display_manager_->GpuReceivedHDCPState(display_id, success, state, + protection_method); } void HostDrmDevice::GpuSetHDCPStateCallback(int64_t display_id, diff --git a/chromium/ui/ozone/platform/drm/host/host_drm_device.h b/chromium/ui/ozone/platform/drm/host/host_drm_device.h index 2484d706c9e..18170a0bc2f 100644 --- a/chromium/ui/ozone/platform/drm/host/host_drm_device.h +++ b/chromium/ui/ozone/platform/drm/host/host_drm_device.h @@ -69,7 +69,10 @@ class HostDrmDevice : public base::RefCountedThreadSafe<HostDrmDevice>, const std::vector<display::DisplayConfigurationParams>& config_requests, display::ConfigureCallback callback) override; bool GpuGetHDCPState(int64_t display_id) override; - bool GpuSetHDCPState(int64_t display_id, display::HDCPState state) override; + bool GpuSetHDCPState( + int64_t display_id, + display::HDCPState state, + display::ContentProtectionMethod protection_method) override; bool GpuSetColorMatrix(int64_t display_id, const std::vector<float>& color_matrix) override; bool GpuSetGammaCorrection( @@ -96,9 +99,11 @@ class HostDrmDevice : public base::RefCountedThreadSafe<HostDrmDevice>, void GpuRefreshNativeDisplaysCallback(MovableDisplaySnapshots displays) const; void GpuTakeDisplayControlCallback(bool success) const; void GpuRelinquishDisplayControlCallback(bool success) const; - void GpuGetHDCPStateCallback(int64_t display_id, - bool success, - display::HDCPState state) const; + void GpuGetHDCPStateCallback( + int64_t display_id, + bool success, + display::HDCPState state, + display::ContentProtectionMethod protection_method) const; void GpuSetHDCPStateCallback(int64_t display_id, bool success) const; void OnGpuServiceLaunchedOnUIThread( diff --git a/chromium/ui/ozone/platform/drm/ozone_platform_gbm.cc b/chromium/ui/ozone/platform/drm/ozone_platform_drm.cc index 4e81e147590..8a6442257df 100644 --- a/chromium/ui/ozone/platform/drm/ozone_platform_gbm.cc +++ b/chromium/ui/ozone/platform/drm/ozone_platform_drm.cc @@ -2,7 +2,7 @@ // 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/ozone_platform_gbm.h" +#include "ui/ozone/platform/drm/ozone_platform_drm.h" #include <gbm.h> #include <stdlib.h> @@ -68,10 +68,10 @@ namespace ui { namespace { -class OzonePlatformGbm : public OzonePlatform { +class OzonePlatformDrm : public OzonePlatform { public: - OzonePlatformGbm() = default; - ~OzonePlatformGbm() override = default; + OzonePlatformDrm() = default; + ~OzonePlatformDrm() override = default; // OzonePlatform: ui::SurfaceFactoryOzone* GetSurfaceFactoryOzone() override { @@ -91,7 +91,7 @@ class OzonePlatformGbm : public OzonePlatform { } GpuPlatformSupportHost* GetGpuPlatformSupportHost() override { - return drm_device_connector_.get(); + return drm_device_connector_.get(); } std::unique_ptr<SystemInputInjector> CreateSystemInputInjector() override { @@ -110,7 +110,7 @@ class OzonePlatformGbm : public OzonePlatform { // method after drm_thread is started. binders->Add<ozone::mojom::DrmDevice>( base::BindRepeating( - &OzonePlatformGbm::CreateDrmDeviceReceiverOnGpuThread, + &OzonePlatformDrm::CreateDrmDeviceReceiverOnGpuThread, weak_factory_.GetWeakPtr()), gpu_task_runner_); } else { @@ -123,7 +123,7 @@ class OzonePlatformGbm : public OzonePlatform { // Binder callbacks should directly run on DRM thread. binders->Add<ozone::mojom::DrmDevice>( base::BindRepeating( - &OzonePlatformGbm::CreateDrmDeviceReceiverOnDrmThread, + &OzonePlatformDrm::CreateDrmDeviceReceiverOnDrmThread, weak_factory_.GetWeakPtr()), drm_thread_proxy_->GetDrmThreadTaskRunner()); } @@ -295,7 +295,7 @@ class OzonePlatformGbm : public OzonePlatform { DrainReceiverRequests(); } else { auto safe_receiver_request_drainer = CreateSafeOnceCallback( - base::BindOnce(&OzonePlatformGbm::DrainReceiverRequests, + base::BindOnce(&OzonePlatformDrm::DrainReceiverRequests, weak_factory_.GetWeakPtr())); drm_thread_proxy_->StartDrmThread( std::move(safe_receiver_request_drainer)); @@ -336,15 +336,15 @@ class OzonePlatformGbm : public OzonePlatform { std::unique_ptr<DrmDisplayHostManager> display_manager_; InitializedHostProperties host_properties_; - base::WeakPtrFactory<OzonePlatformGbm> weak_factory_{this}; - - DISALLOW_COPY_AND_ASSIGN(OzonePlatformGbm); + base::WeakPtrFactory<OzonePlatformDrm> weak_factory_{this}; + OzonePlatformDrm(const OzonePlatformDrm&) = delete; + OzonePlatformDrm& operator=(const OzonePlatformDrm&) = delete; }; } // namespace -OzonePlatform* CreateOzonePlatformGbm() { - return new OzonePlatformGbm; +OzonePlatform* CreateOzonePlatformDrm() { + return new OzonePlatformDrm; } } // namespace ui diff --git a/chromium/ui/ozone/platform/drm/ozone_platform_gbm.h b/chromium/ui/ozone/platform/drm/ozone_platform_drm.h index 324535d91d4..39cfa0d87c5 100644 --- a/chromium/ui/ozone/platform/drm/ozone_platform_gbm.h +++ b/chromium/ui/ozone/platform/drm/ozone_platform_drm.h @@ -2,16 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_OZONE_PLATFORM_DRM_OZONE_PLATFORM_GBM_H_ -#define UI_OZONE_PLATFORM_DRM_OZONE_PLATFORM_GBM_H_ +#ifndef UI_OZONE_PLATFORM_DRM_OZONE_PLATFORM_DRM_H_ +#define UI_OZONE_PLATFORM_DRM_OZONE_PLATFORM_DRM_H_ namespace ui { class OzonePlatform; // Constructor hook for use in ozone_platform_list.cc -OzonePlatform* CreateOzonePlatformGbm(); +OzonePlatform* CreateOzonePlatformDrm(); } // namespace ui -#endif // UI_OZONE_PLATFORM_DRM_OZONE_PLATFORM_GBM_H_ +#endif // UI_OZONE_PLATFORM_DRM_OZONE_PLATFORM_DRM_H_ diff --git a/chromium/ui/ozone/platform/scenic/BUILD.gn b/chromium/ui/ozone/platform/scenic/BUILD.gn index 32792aaf31a..616316662ad 100644 --- a/chromium/ui/ozone/platform/scenic/BUILD.gn +++ b/chromium/ui/ozone/platform/scenic/BUILD.gn @@ -18,6 +18,8 @@ source_set("scenic") { "scenic_gpu_host.h", "scenic_gpu_service.cc", "scenic_gpu_service.h", + "scenic_overlay_view.cc", + "scenic_overlay_view.h", "scenic_screen.cc", "scenic_screen.h", "scenic_surface.cc", diff --git a/chromium/ui/ozone/platform/scenic/ozone_platform_scenic.cc b/chromium/ui/ozone/platform/scenic/ozone_platform_scenic.cc index ec639a62722..b7c7dae3c40 100644 --- a/chromium/ui/ozone/platform/scenic/ozone_platform_scenic.cc +++ b/chromium/ui/ozone/platform/scenic/ozone_platform_scenic.cc @@ -12,6 +12,7 @@ #include "base/macros.h" #include "base/memory/ptr_util.h" #include "base/message_loop/message_pump_type.h" +#include "base/no_destructor.h" #include "base/notreached.h" #include "base/task/current_thread.h" #include "mojo/public/cpp/bindings/pending_remote.h" @@ -43,14 +44,6 @@ namespace ui { namespace { -constexpr OzonePlatform::PlatformProperties kScenicPlatformProperties{ - .needs_view_token = true, - .custom_frame_pref_default = false, - .use_system_title_bar = false, - .message_pump_type_for_gpu = base::MessagePumpType::IO, - .supports_vulkan_swap_chain = true, -}; - class ScenicPlatformEventSource : public ui::PlatformEventSource { public: ScenicPlatformEventSource() = default; @@ -105,7 +98,17 @@ class OzonePlatformScenic : public OzonePlatform, } const PlatformProperties& GetPlatformProperties() override { - return kScenicPlatformProperties; + static base::NoDestructor<OzonePlatform::PlatformProperties> properties; + static bool initialised = false; + if (!initialised) { + properties->needs_view_token = true; + properties->message_pump_type_for_gpu = base::MessagePumpType::IO; + properties->supports_vulkan_swap_chain = true; + + initialised = true; + } + + return *properties; } std::unique_ptr<display::NativeDisplayDelegate> CreateNativeDisplayDelegate() diff --git a/chromium/ui/ozone/platform/scenic/scenic_overlay_view.cc b/chromium/ui/ozone/platform/scenic/scenic_overlay_view.cc new file mode 100644 index 00000000000..15e00dcea3a --- /dev/null +++ b/chromium/ui/ozone/platform/scenic/scenic_overlay_view.cc @@ -0,0 +1,77 @@ +// Copyright 2020 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/scenic/scenic_overlay_view.h" + +#include <lib/ui/scenic/cpp/commands.h> +#include <lib/ui/scenic/cpp/view_token_pair.h> + +#include "base/fuchsia/fuchsia_logging.h" + +namespace ui { + +namespace { + +// |buffer_collection_id| that is passed to ImagePipe::AddBufferCollection() if +// SysmemBufferCollection instance is registered with one. +static const uint32_t kImagePipeBufferCollectionId = 1; +static const std::string kSessionDebugName = "chromium scenic overlay"; + +fuchsia::ui::views::ViewToken CreateViewToken( + fuchsia::ui::views::ViewHolderToken* holder_token) { + auto token_pair = scenic::ViewTokenPair::New(); + *holder_token = std::move(token_pair.view_holder_token); + return std::move(token_pair.view_token); +} + +} // namespace + +ScenicOverlayView::ScenicOverlayView( + scenic::SessionPtrAndListenerRequest session_and_listener_request) + : scenic_session_(std::move(session_and_listener_request)), + view_(&scenic_session_, + CreateViewToken(&view_holder_token_), + kSessionDebugName) { + scenic_session_.SetDebugName(kSessionDebugName); + scenic_session_.set_error_handler([](zx_status_t status) { + ZX_LOG(FATAL, status) << "Lost connection to scenic session."; + }); +} + +ScenicOverlayView::~ScenicOverlayView() = default; + +void ScenicOverlayView::Initialize( + fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken> + collection_token) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + uint32_t image_pipe_id = scenic_session_.AllocResourceId(); + scenic_session_.Enqueue( + scenic::NewCreateImagePipe2Cmd(image_pipe_id, image_pipe_.NewRequest())); + image_pipe_.set_error_handler([](zx_status_t status) { + ZX_LOG(FATAL, status) << "ImagePipe disconnected"; + }); + + scenic::Material image_material(&scenic_session_); + image_material.SetTexture(image_pipe_id); + + scenic::ShapeNode shape(&scenic_session_); + shape.SetShape(scenic::Rectangle(&scenic_session_, 1.f, 1.f)); + shape.SetMaterial(image_material); + + view_.AddChild(shape); + scenic_session_.ReleaseResource(image_pipe_id); + scenic_session_.Present2( + /*requested_presentation_time=*/0, + /*requested_prediction_span=*/0, + [](fuchsia::scenic::scheduling::FuturePresentationTimes info) {}); + + // Since there is one ImagePipe for each BufferCollection, it is ok to use a + // fixed buffer_collection_id. + // TODO(emircan): Consider using one ImagePipe per video decoder instead. + image_pipe_->AddBufferCollection(kImagePipeBufferCollectionId, + std::move(collection_token)); + LOG(ERROR) << __func__; +} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/scenic/scenic_overlay_view.h b/chromium/ui/ozone/platform/scenic/scenic_overlay_view.h new file mode 100644 index 00000000000..ee32472ea9d --- /dev/null +++ b/chromium/ui/ozone/platform/scenic/scenic_overlay_view.h @@ -0,0 +1,43 @@ +// Copyright 2020 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. + +#ifndef UI_OZONE_PLATFORM_SCENIC_SCENIC_OVERLAY_VIEW_H_ +#define UI_OZONE_PLATFORM_SCENIC_SCENIC_OVERLAY_VIEW_H_ + +#include <fuchsia/ui/scenic/cpp/fidl.h> +#include <lib/ui/scenic/cpp/resources.h> +#include <lib/ui/scenic/cpp/session.h> + +#include "base/threading/thread_checker.h" + +namespace ui { + +// Holder for scenic::Session and scenic::View that owns an Image Pipe. +// +// This class allows the callers to access an ImagePipe and a scenic::View that +// displays only that ImagePipe. This is used inside SysmemBufferCollection +// instances to display overlays. +class ScenicOverlayView { + public: + ScenicOverlayView( + scenic::SessionPtrAndListenerRequest session_and_listener_request); + ~ScenicOverlayView(); + ScenicOverlayView(const ScenicOverlayView&) = delete; + ScenicOverlayView& operator=(const ScenicOverlayView&) = delete; + + void Initialize(fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken> + collection_token); + + private: + scenic::Session scenic_session_; + fuchsia::ui::views::ViewHolderToken view_holder_token_; + scenic::View view_; + fuchsia::images::ImagePipe2Ptr image_pipe_; + + THREAD_CHECKER(thread_checker_); +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_SCENIC_SCENIC_OVERLAY_VIEW_H_ diff --git a/chromium/ui/ozone/platform/scenic/scenic_surface_factory.cc b/chromium/ui/ozone/platform/scenic/scenic_surface_factory.cc index 0b5749ee013..ba0b5e7a2da 100644 --- a/chromium/ui/ozone/platform/scenic/scenic_surface_factory.cc +++ b/chromium/ui/ozone/platform/scenic/scenic_surface_factory.cc @@ -138,6 +138,7 @@ fuchsia::sysmem::AllocatorHandle ConnectSysmemAllocator() { ScenicSurfaceFactory::ScenicSurfaceFactory() : egl_implementation_(std::make_unique<GLOzoneEGLScenic>(this)), + sysmem_buffer_manager_(this), weak_ptr_factory_(this) {} ScenicSurfaceFactory::~ScenicSurfaceFactory() { diff --git a/chromium/ui/ozone/platform/scenic/scenic_surface_factory.h b/chromium/ui/ozone/platform/scenic/scenic_surface_factory.h index 0a2e11de524..b0454ac45cb 100644 --- a/chromium/ui/ozone/platform/scenic/scenic_surface_factory.h +++ b/chromium/ui/ozone/platform/scenic/scenic_surface_factory.h @@ -85,10 +85,10 @@ class ScenicSurfaceFactory : public SurfaceFactoryOzone { ScenicSurface* GetSurface(gfx::AcceleratedWidget widget) LOCKS_EXCLUDED(surface_lock_); - private: // Creates a new scenic session on any thread. scenic::SessionPtrAndListenerRequest CreateScenicSession(); + private: // Links a surface to its parent window in the host process. void AttachSurfaceToWindow( gfx::AcceleratedWidget window, diff --git a/chromium/ui/ozone/platform/scenic/sysmem_buffer_collection.cc b/chromium/ui/ozone/platform/scenic/sysmem_buffer_collection.cc index 300e188712e..e84f242d08e 100644 --- a/chromium/ui/ozone/platform/scenic/sysmem_buffer_collection.cc +++ b/chromium/ui/ozone/platform/scenic/sysmem_buffer_collection.cc @@ -10,6 +10,7 @@ #include "gpu/vulkan/vulkan_function_pointers.h" #include "ui/gfx/buffer_format_util.h" #include "ui/gfx/native_pixmap.h" +#include "ui/ozone/platform/scenic/scenic_surface_factory.h" namespace ui { @@ -129,13 +130,15 @@ SysmemBufferCollection::SysmemBufferCollection(gfx::SysmemBufferCollectionId id) bool SysmemBufferCollection::Initialize( fuchsia::sysmem::Allocator_Sync* allocator, + ScenicSurfaceFactory* scenic_surface_factory, zx::channel token_handle, gfx::Size size, gfx::BufferFormat format, gfx::BufferUsage usage, VkDevice vk_device, size_t min_buffer_count, - bool force_protected) { + bool force_protected, + bool register_with_image_pipe) { DCHECK(IsNativePixmapConfigSupported(format, usage)); DCHECK(!collection_); DCHECK(!vk_buffer_collection_); @@ -165,6 +168,10 @@ bool SysmemBufferCollection::Initialize( vk_device_ = vk_device; is_protected_ = force_protected; + if (register_with_image_pipe) { + scenic_overlay_view_.emplace(scenic_surface_factory->CreateScenicSession()); + } + fuchsia::sysmem::BufferCollectionTokenSyncPtr collection_token; if (token_handle) { collection_token.Bind(std::move(token_handle)); @@ -365,12 +372,26 @@ bool SysmemBufferCollection::InitializeInternal( collection_token_for_vulkan; collection_token->Duplicate(ZX_RIGHT_SAME_RIGHTS, collection_token_for_vulkan.NewRequest()); + + // Duplicate one more token for Scenic if this collection can be used as an + // overlay. + fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken> + collection_token_for_scenic; + if (scenic_overlay_view_.has_value()) { + collection_token->Duplicate(ZX_RIGHT_SAME_RIGHTS, + collection_token_for_scenic.NewRequest()); + } + zx_status_t status = collection_token->Sync(); if (status != ZX_OK) { ZX_DLOG(ERROR, status) << "fuchsia.sysmem.BufferCollectionToken.Sync()"; return false; } + if (scenic_overlay_view_.has_value()) { + scenic_overlay_view_->Initialize(std::move(collection_token_for_scenic)); + } + status = allocator->BindSharedCollection(std::move(collection_token), collection_.NewRequest()); if (status != ZX_OK) { diff --git a/chromium/ui/ozone/platform/scenic/sysmem_buffer_collection.h b/chromium/ui/ozone/platform/scenic/sysmem_buffer_collection.h index 38f9e12e1bf..07cb267c082 100644 --- a/chromium/ui/ozone/platform/scenic/sysmem_buffer_collection.h +++ b/chromium/ui/ozone/platform/scenic/sysmem_buffer_collection.h @@ -6,17 +6,20 @@ #define UI_OZONE_PLATFORM_SCENIC_SYSMEM_BUFFER_COLLECTION_H_ #include <fuchsia/sysmem/cpp/fidl.h> +#include <lib/ui/scenic/cpp/session.h> #include <vulkan/vulkan.h> #include "base/callback.h" #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "base/optional.h" #include "base/threading/thread_checker.h" #include "gpu/ipc/common/vulkan_ycbcr_info.h" #include "gpu/vulkan/fuchsia/vulkan_fuchsia_ext.h" #include "ui/gfx/buffer_types.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/native_pixmap_handle.h" +#include "ui/ozone/platform/scenic/scenic_overlay_view.h" namespace gfx { class NativePixmap; @@ -24,6 +27,8 @@ class NativePixmap; namespace ui { +class ScenicSurfaceFactory; + // SysmemBufferCollection keeps sysmem.BufferCollection interface along with the // corresponding VkBufferCollectionFUCHSIA. It allows to create either // gfx::NativePixmap or VkImage from the buffers in the collection. @@ -44,14 +49,18 @@ class SysmemBufferCollection // collection is created. |size| may be empty. In that case |token_handle| // must not be null and the image size is determined by the other sysmem // participants. + // If |register_with_image_pipe| is true, new ScenicOverlayView instance is + // created and |token_handle| gets duplicated to be added to its ImagePipe. bool Initialize(fuchsia::sysmem::Allocator_Sync* allocator, + ScenicSurfaceFactory* scenic_surface_factory, zx::channel token_handle, gfx::Size size, gfx::BufferFormat format, gfx::BufferUsage usage, VkDevice vk_device, size_t min_buffer_count, - bool force_protected); + bool force_protected, + bool register_with_image_pipe); // Must not be called more than once. void SetOnDeletedCallback(base::OnceClosure on_deleted); @@ -117,6 +126,11 @@ class SysmemBufferCollection // that is referenced by |collection_|. VkBufferCollectionFUCHSIA vk_buffer_collection_ = VK_NULL_HANDLE; + // If ScenicOverlayView is created and its ImagePipe is added as a participant + // in buffer allocation negotiations, the associated images can be displayed + // as overlays. + base::Optional<ScenicOverlayView> scenic_overlay_view_; + // Thread checker used to verify that CreateVkImage() is always called from // the same thread. It may be unsafe to use vk_buffer_collection_ on different // threads. @@ -133,4 +147,4 @@ class SysmemBufferCollection } // namespace ui -#endif // UI_OZONE_PLATFORM_SCENIC_SYSMEM_BUFFER_COLLECTION_H_
\ No newline at end of file +#endif // UI_OZONE_PLATFORM_SCENIC_SYSMEM_BUFFER_COLLECTION_H_ diff --git a/chromium/ui/ozone/platform/scenic/sysmem_buffer_manager.cc b/chromium/ui/ozone/platform/scenic/sysmem_buffer_manager.cc index 8138ded3e55..8d55a71262b 100644 --- a/chromium/ui/ozone/platform/scenic/sysmem_buffer_manager.cc +++ b/chromium/ui/ozone/platform/scenic/sysmem_buffer_manager.cc @@ -12,7 +12,9 @@ namespace ui { -SysmemBufferManager::SysmemBufferManager() = default; +SysmemBufferManager::SysmemBufferManager( + ScenicSurfaceFactory* scenic_surface_factory) + : scenic_surface_factory_(scenic_surface_factory) {} SysmemBufferManager::~SysmemBufferManager() { Shutdown(); @@ -39,9 +41,11 @@ scoped_refptr<SysmemBufferCollection> SysmemBufferManager::CreateCollection( gfx::BufferUsage usage, size_t min_buffer_count) { auto result = base::MakeRefCounted<SysmemBufferCollection>(); - if (!result->Initialize(allocator_.get(), /*token_channel=*/zx::channel(), - size, format, usage, vk_device, min_buffer_count, - /*force_protected=*/false)) { + if (!result->Initialize(allocator_.get(), scenic_surface_factory_, + /*token_channel=*/zx::channel(), size, format, usage, + vk_device, min_buffer_count, + /*force_protected=*/false, + /*register_with_image_pipe=*/false)) { return nullptr; } RegisterCollection(result.get()); @@ -57,11 +61,13 @@ SysmemBufferManager::ImportSysmemBufferCollection( gfx::BufferFormat format, gfx::BufferUsage usage, size_t min_buffer_count, - bool force_protected) { + bool force_protected, + bool register_with_image_pipe) { auto result = base::MakeRefCounted<SysmemBufferCollection>(id); - if (!result->Initialize(allocator_.get(), std::move(token), size, format, - usage, vk_device, min_buffer_count, - force_protected)) { + if (!result->Initialize(allocator_.get(), scenic_surface_factory_, + std::move(token), size, format, usage, vk_device, + min_buffer_count, force_protected, + register_with_image_pipe)) { return nullptr; } RegisterCollection(result.get()); diff --git a/chromium/ui/ozone/platform/scenic/sysmem_buffer_manager.h b/chromium/ui/ozone/platform/scenic/sysmem_buffer_manager.h index 6845d8d8df7..eb2dbaa6f5f 100644 --- a/chromium/ui/ozone/platform/scenic/sysmem_buffer_manager.h +++ b/chromium/ui/ozone/platform/scenic/sysmem_buffer_manager.h @@ -6,6 +6,7 @@ #define UI_OZONE_PLATFORM_SCENIC_SYSMEM_BUFFER_MANAGER_H_ #include <fuchsia/sysmem/cpp/fidl.h> +#include <lib/ui/scenic/cpp/session.h> #include <vulkan/vulkan.h> #include <unordered_map> @@ -23,10 +24,11 @@ namespace ui { class SysmemBufferCollection; +class ScenicSurfaceFactory; class SysmemBufferManager { public: - explicit SysmemBufferManager(); + explicit SysmemBufferManager(ScenicSurfaceFactory* scenic_surface_factory); ~SysmemBufferManager(); // Initializes the buffer manager with a connection to the sysmem service. @@ -51,7 +53,8 @@ class SysmemBufferManager { gfx::BufferFormat format, gfx::BufferUsage usage, size_t min_buffer_count, - bool force_protected); + bool force_protected, + bool register_with_image_pipe); scoped_refptr<SysmemBufferCollection> GetCollectionById( gfx::SysmemBufferCollectionId id); @@ -60,6 +63,7 @@ class SysmemBufferManager { void RegisterCollection(SysmemBufferCollection* collection); void OnCollectionDestroyed(gfx::SysmemBufferCollectionId id); + ScenicSurfaceFactory* const scenic_surface_factory_; fuchsia::sysmem::AllocatorSyncPtr allocator_; base::small_map<std::unordered_map<gfx::SysmemBufferCollectionId, diff --git a/chromium/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc b/chromium/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc index 6f3d23999e5..507c9032f24 100644 --- a/chromium/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc +++ b/chromium/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc @@ -302,14 +302,16 @@ VulkanImplementationScenic::RegisterSysmemBufferCollection( gfx::BufferFormat format, gfx::BufferUsage usage, gfx::Size size, - size_t min_buffer_count) { + size_t min_buffer_count, + bool register_with_image_pipe) { // SCANOUT images must be protected in protected mode. bool force_protected = usage == gfx::BufferUsage::SCANOUT && enforce_protected_memory(); + fuchsia::images::ImagePipe2Ptr image_pipe = nullptr; auto buffer_collection = sysmem_buffer_manager_->ImportSysmemBufferCollection( device, id, std::move(token), size, format, usage, min_buffer_count, - force_protected); + force_protected, register_with_image_pipe); if (!buffer_collection) return nullptr; diff --git a/chromium/ui/ozone/platform/scenic/vulkan_implementation_scenic.h b/chromium/ui/ozone/platform/scenic/vulkan_implementation_scenic.h index d92869e733d..04c371db8d1 100644 --- a/chromium/ui/ozone/platform/scenic/vulkan_implementation_scenic.h +++ b/chromium/ui/ozone/platform/scenic/vulkan_implementation_scenic.h @@ -60,7 +60,8 @@ class VulkanImplementationScenic : public gpu::VulkanImplementation { gfx::BufferFormat format, gfx::BufferUsage usage, gfx::Size size, - size_t min_buffer_count) override; + size_t min_buffer_count, + bool register_with_image_pipe) override; private: ScenicSurfaceFactory* const scenic_surface_factory_; diff --git a/chromium/ui/ozone/platform/wayland/BUILD.gn b/chromium/ui/ozone/platform/wayland/BUILD.gn index 6b87d5ca7c6..e61680b4a38 100644 --- a/chromium/ui/ozone/platform/wayland/BUILD.gn +++ b/chromium/ui/ozone/platform/wayland/BUILD.gn @@ -4,10 +4,12 @@ visibility = [ "//ui/ozone/*" ] +import("//build/config/chromeos/ui_mode.gni") import("//build/config/linux/gtk/gtk.gni") import("//build/config/linux/pkg_config.gni") import("//gpu/vulkan/features.gni") import("//testing/libfuzzer/fuzzer_test.gni") +import("//third_party/wayland/features.gni") import("//ui/ozone/platform/wayland/wayland.gni") source_set("wayland") { @@ -16,6 +18,8 @@ source_set("wayland") { "client_native_pixmap_factory_wayland.h", "common/data_util.cc", "common/data_util.h", + "common/wayland.cc", + "common/wayland.h", "common/wayland_object.cc", "common/wayland_object.h", "common/wayland_util.cc", @@ -122,6 +126,8 @@ source_set("wayland") { "host/wayland_window_observer.h", "host/wayland_zwp_linux_dmabuf.cc", "host/wayland_zwp_linux_dmabuf.h", + "host/xdg_foreign_wrapper.cc", + "host/xdg_foreign_wrapper.h", "host/xdg_popup_wrapper_impl.cc", "host/xdg_popup_wrapper_impl.h", "host/xdg_surface_wrapper_impl.cc", @@ -140,19 +146,22 @@ source_set("wayland") { deps = [ "//base", - "//build:lacros_buildflags", + "//build:chromeos_buildflags", "//build/config/linux/libdrm", "//components/exo/wayland/protocol:aura_shell_protocol", "//mojo/public/cpp/bindings", "//mojo/public/cpp/system", "//skia", - "//third_party/wayland:wayland_egl", "//third_party/wayland-protocols:gtk_primary_selection_protocol", "//third_party/wayland-protocols:keyboard_extension_protocol", "//third_party/wayland-protocols:linux_dmabuf_protocol", + "//third_party/wayland-protocols:linux_explicit_synchronization_protocol", "//third_party/wayland-protocols:presentation_time_protocol", "//third_party/wayland-protocols:text_input_protocol", + "//third_party/wayland-protocols:viewporter_protocol", "//third_party/wayland-protocols:wayland_drm_protocol", + "//third_party/wayland-protocols:xdg_decoration_protocol", + "//third_party/wayland-protocols:xdg_foreign", "//third_party/wayland-protocols:xdg_shell_protocol", "//ui/base", "//ui/base:buildflags", @@ -177,18 +186,6 @@ source_set("wayland") { "//ui/platform_window/wm", ] - # TODO(msisov): we should do the following once support for Trusty and Jessie - # are dropped: - # 1. Remove libs=["wayland-client"] and - # add deps+=["//third_party/wayland:wayland_client"] - # 2. Set use_system_libwayland=true for is_desktop_linux. - libs = [] - if (!is_chromeos) { - libs = [ "wayland-client" ] - } else { - deps += [ "//third_party/wayland:wayland_client" ] - } - if (is_linux && !is_chromeos) { deps += [ "//ui/base/ime/linux" ] } @@ -204,8 +201,23 @@ source_set("wayland") { ] } + # TODO(crbug.com/1052397): Rename chromeos_is_browser_only to is_lacros. + if (chromeos_is_browser_only) { + deps += [ "//chromeos/crosapi/cpp" ] + } + defines = [ "OZONE_IMPLEMENTATION" ] + if (use_system_libwayland) { + defines += [ "USE_LIBWAYLAND_STUBS" ] + deps += [ "//third_party/wayland:wayland_stubs" ] + } else { + deps += [ + "//third_party/wayland:wayland_client", + "//third_party/wayland:wayland_egl", + ] + } + if (use_wayland_gbm) { defines += [ "WAYLAND_GBM" ] sources += [ @@ -291,6 +303,10 @@ source_set("test_support") { "test/test_subsurface.h", "test/test_touch.cc", "test/test_touch.h", + "test/test_viewport.cc", + "test/test_viewport.h", + "test/test_viewporter.cc", + "test/test_viewporter.h", "test/test_wayland_server_thread.cc", "test/test_wayland_server_thread.h", "test/test_xdg_popup.cc", @@ -313,6 +329,7 @@ source_set("test_support") { "//third_party/wayland-protocols:linux_dmabuf_protocol", "//third_party/wayland-protocols:presentation_time_protocol", "//third_party/wayland-protocols:text_input_protocol", + "//third_party/wayland-protocols:viewporter_protocol", "//third_party/wayland-protocols:xdg_shell_protocol", "//ui/gfx/geometry:geometry", ] diff --git a/chromium/ui/ozone/platform/wayland/DEPS b/chromium/ui/ozone/platform/wayland/DEPS index 44ef7beabfd..1eeae79d79e 100644 --- a/chromium/ui/ozone/platform/wayland/DEPS +++ b/chromium/ui/ozone/platform/wayland/DEPS @@ -4,6 +4,7 @@ include_rules = [ "+ui/base/ui_base_features.h", "+ui/gtk/gtk_ui_delegate.h", "+mojo/public", + "+third_party/wayland", "+ui/base/clipboard/clipboard_constants.h", "+ui/base/dragdrop/drag_drop_types.h", "+ui/base/dragdrop/file_info/file_info.h", diff --git a/chromium/ui/ozone/platform/wayland/common/wayland.cc b/chromium/ui/ozone/platform/wayland/common/wayland.cc new file mode 100644 index 00000000000..8a20e3a7be3 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/common/wayland.cc @@ -0,0 +1,24 @@ +// Copyright 2020 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/wayland/common/wayland.h" + +namespace wl { + +void* bind_registry(struct wl_registry* registry, + uint32_t name, + const struct wl_interface* interface, + uint32_t version) { + if (dlsym(RTLD_DEFAULT, "wl_proxy_marshal_constructor_versioned")) { + return wl_registry_bind(registry, name, interface, version); + } else { + return wl_proxy_marshal_constructor(reinterpret_cast<wl_proxy*>(registry), + WL_REGISTRY_BIND, interface, name, + interface->name, version, nullptr); + } + NOTREACHED(); + return nullptr; +} + +} // namespace wl diff --git a/chromium/ui/ozone/platform/wayland/common/wayland.h b/chromium/ui/ozone/platform/wayland/common/wayland.h new file mode 100644 index 00000000000..c645346ddcb --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/common/wayland.h @@ -0,0 +1,31 @@ +// Copyright 2020 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. + +#ifndef UI_OZONE_PLATFORM_WAYLAND_COMMON_WAYLAND_H_ +#define UI_OZONE_PLATFORM_WAYLAND_COMMON_WAYLAND_H_ + +#include <dlfcn.h> +// This header includes wayland-client-core.h and wayland-client-protocol.h +#include <wayland-client.h> + +#include "base/notreached.h" + +namespace wl { + +template <typename T> +uint32_t get_version_of_object(T* obj) { + if (dlsym(RTLD_DEFAULT, "wl_proxy_get_version")) + return wl_proxy_get_version(reinterpret_cast<wl_proxy*>(obj)); + // Older version of the libwayland-client didn't support version of objects. + return 0; +} + +void* bind_registry(struct wl_registry* registry, + uint32_t name, + const struct wl_interface* interface, + uint32_t version); + +} // namespace wl + +#endif // UI_OZONE_PLATFORM_WAYLAND_COMMON_WAYLAND_H_ diff --git a/chromium/ui/ozone/platform/wayland/common/wayland_object.cc b/chromium/ui/ozone/platform/wayland/common/wayland_object.cc index a71f25d42b7..6d9c7536c7e 100644 --- a/chromium/ui/ozone/platform/wayland/common/wayland_object.cc +++ b/chromium/ui/ozone/platform/wayland/common/wayland_object.cc @@ -8,10 +8,13 @@ #include <gtk-primary-selection-client-protocol.h> #include <keyboard-extension-unstable-v1-client-protocol.h> #include <linux-dmabuf-unstable-v1-client-protocol.h> +#include <linux-explicit-synchronization-unstable-v1-client-protocol.h> #include <presentation-time-client-protocol.h> #include <text-input-unstable-v1-client-protocol.h> -#include <wayland-client.h> +#include <viewporter-client-protocol.h> #include <wayland-drm-client-protocol.h> +#include <xdg-decoration-unstable-v1-client-protocol.h> +#include <xdg-foreign-unstable-v1-client-protocol.h> #include <xdg-shell-client-protocol.h> #include <xdg-shell-unstable-v6-client-protocol.h> @@ -19,35 +22,35 @@ namespace wl { namespace { void delete_keyboard(wl_keyboard* keyboard) { - if (wl_keyboard_get_version(keyboard) >= WL_KEYBOARD_RELEASE_SINCE_VERSION) + if (wl::get_version_of_object(keyboard) >= WL_KEYBOARD_RELEASE_SINCE_VERSION) wl_keyboard_release(keyboard); else wl_keyboard_destroy(keyboard); } void delete_pointer(wl_pointer* pointer) { - if (wl_pointer_get_version(pointer) >= WL_POINTER_RELEASE_SINCE_VERSION) + if (wl::get_version_of_object(pointer) >= WL_POINTER_RELEASE_SINCE_VERSION) wl_pointer_release(pointer); else wl_pointer_destroy(pointer); } void delete_seat(wl_seat* seat) { - if (wl_seat_get_version(seat) >= WL_SEAT_RELEASE_SINCE_VERSION) + if (wl::get_version_of_object(seat) >= WL_SEAT_RELEASE_SINCE_VERSION) wl_seat_release(seat); else wl_seat_destroy(seat); } void delete_touch(wl_touch* touch) { - if (wl_touch_get_version(touch) >= WL_TOUCH_RELEASE_SINCE_VERSION) + if (wl::get_version_of_object(touch) >= WL_TOUCH_RELEASE_SINCE_VERSION) wl_touch_release(touch); else wl_touch_destroy(touch); } void delete_data_device(wl_data_device* data_device) { - if (wl_data_device_get_version(data_device) >= + if (wl::get_version_of_object(data_device) >= WL_DATA_DEVICE_RELEASE_SINCE_VERSION) { wl_data_device_release(data_device); } else { @@ -57,6 +60,16 @@ void delete_data_device(wl_data_device* data_device) { } // namespace +const wl_interface* ObjectTraits<zxdg_decoration_manager_v1>::interface = + &zxdg_decoration_manager_v1_interface; +void (*ObjectTraits<zxdg_decoration_manager_v1>::deleter)( + zxdg_decoration_manager_v1*) = &zxdg_decoration_manager_v1_destroy; + +const wl_interface* ObjectTraits<zxdg_toplevel_decoration_v1>::interface = + &zxdg_toplevel_decoration_v1_interface; +void (*ObjectTraits<zxdg_toplevel_decoration_v1>::deleter)( + zxdg_toplevel_decoration_v1*) = &zxdg_toplevel_decoration_v1_destroy; + const wl_interface* ObjectTraits<gtk_primary_selection_device_manager>::interface = >k_primary_selection_device_manager_interface; @@ -170,6 +183,15 @@ const wl_interface* ObjectTraits<struct wp_presentation_feedback>::interface = void (*ObjectTraits<struct wp_presentation_feedback>::deleter)( struct wp_presentation_feedback*) = &wp_presentation_feedback_destroy; +const wl_interface* ObjectTraits<wp_viewport>::interface = + &wp_viewport_interface; +void (*ObjectTraits<wp_viewport>::deleter)(wp_viewport*) = &wp_viewport_destroy; + +const wl_interface* ObjectTraits<wp_viewporter>::interface = + &wp_viewporter_interface; +void (*ObjectTraits<wp_viewporter>::deleter)(wp_viewporter*) = + &wp_viewporter_destroy; + const wl_interface* ObjectTraits<xdg_wm_base>::interface = &xdg_wm_base_interface; void (*ObjectTraits<xdg_wm_base>::deleter)(xdg_wm_base*) = &xdg_wm_base_destroy; @@ -215,6 +237,25 @@ const wl_interface* ObjectTraits<zwp_linux_dmabuf_v1>::interface = void (*ObjectTraits<zwp_linux_dmabuf_v1>::deleter)(zwp_linux_dmabuf_v1*) = &zwp_linux_dmabuf_v1_destroy; +const wl_interface* ObjectTraits<zwp_linux_buffer_release_v1>::interface = + &zwp_linux_buffer_release_v1_interface; +void (*ObjectTraits<zwp_linux_buffer_release_v1>::deleter)( + zwp_linux_buffer_release_v1*) = &zwp_linux_buffer_release_v1_destroy; + +const wl_interface* + ObjectTraits<zwp_linux_explicit_synchronization_v1>::interface = + &zwp_linux_explicit_synchronization_v1_interface; +void (*ObjectTraits<zwp_linux_explicit_synchronization_v1>::deleter)( + zwp_linux_explicit_synchronization_v1*) = + &zwp_linux_explicit_synchronization_v1_destroy; + +const wl_interface* + ObjectTraits<zwp_linux_surface_synchronization_v1>::interface = + &zwp_linux_surface_synchronization_v1_interface; +void (*ObjectTraits<zwp_linux_surface_synchronization_v1>::deleter)( + zwp_linux_surface_synchronization_v1*) = + &zwp_linux_surface_synchronization_v1_destroy; + const wl_interface* ObjectTraits<zxdg_shell_v6>::interface = &zxdg_shell_v6_interface; void (*ObjectTraits<zxdg_shell_v6>::deleter)(zxdg_shell_v6*) = @@ -250,4 +291,14 @@ const wl_interface* ObjectTraits<zwp_text_input_v1>::interface = void (*ObjectTraits<zwp_text_input_v1>::deleter)(zwp_text_input_v1*) = &zwp_text_input_v1_destroy; +const wl_interface* ObjectTraits<zxdg_exporter_v1>::interface = + &zxdg_exporter_v1_interface; +void (*ObjectTraits<zxdg_exporter_v1>::deleter)(zxdg_exporter_v1*) = + &zxdg_exporter_v1_destroy; + +const wl_interface* ObjectTraits<zxdg_exported_v1>::interface = + &zxdg_exported_v1_interface; +void (*ObjectTraits<zxdg_exported_v1>::deleter)(zxdg_exported_v1*) = + &zxdg_exported_v1_destroy; + } // namespace wl diff --git a/chromium/ui/ozone/platform/wayland/common/wayland_object.h b/chromium/ui/ozone/platform/wayland/common/wayland_object.h index faabaed2de3..bfc61f88f84 100644 --- a/chromium/ui/ozone/platform/wayland/common/wayland_object.h +++ b/chromium/ui/ozone/platform/wayland/common/wayland_object.h @@ -5,9 +5,10 @@ #ifndef UI_OZONE_PLATFORM_WAYLAND_COMMON_WAYLAND_OBJECT_H_ #define UI_OZONE_PLATFORM_WAYLAND_COMMON_WAYLAND_OBJECT_H_ -#include <wayland-client-core.h> #include <memory> +#include "ui/ozone/platform/wayland/common/wayland.h" + struct gtk_primary_selection_device; struct gtk_primary_selection_device_manager; struct gtk_primary_selection_offer; @@ -34,6 +35,8 @@ struct wl_surface; struct wl_touch; struct wp_presentation; struct wp_presentation_feedback; +struct wp_viewport; +struct wp_viewporter; struct xdg_wm_base; struct xdg_surface; struct xdg_toplevel; @@ -44,6 +47,9 @@ struct zaura_surface; struct zcr_keyboard_extension_v1; struct zcr_extended_keyboard_v1; struct zwp_linux_dmabuf_v1; +struct zwp_linux_buffer_release_v1; +struct zwp_linux_explicit_synchronization_v1; +struct zwp_linux_surface_synchronization_v1; struct zxdg_shell_v6; struct zxdg_surface_v6; struct zxdg_toplevel_v6; @@ -51,6 +57,10 @@ struct zxdg_popup_v6; struct zxdg_positioner_v6; struct zwp_text_input_manager_v1; struct zwp_text_input_v1; +struct zxdg_exporter_v1; +struct zxdg_exported_v1; +struct zxdg_decoration_manager_v1; +struct zxdg_toplevel_decoration_v1; namespace wl { @@ -58,6 +68,18 @@ template <typename T> struct ObjectTraits; template <> +struct ObjectTraits<zxdg_decoration_manager_v1> { + static const wl_interface* interface; + static void (*deleter)(zxdg_decoration_manager_v1*); +}; + +template <> +struct ObjectTraits<zxdg_toplevel_decoration_v1> { + static const wl_interface* interface; + static void (*deleter)(zxdg_toplevel_decoration_v1*); +}; + +template <> struct ObjectTraits<gtk_primary_selection_device_manager> { static const wl_interface* interface; static void (*deleter)(gtk_primary_selection_device_manager*); @@ -220,6 +242,18 @@ struct ObjectTraits<wp_presentation_feedback> { }; template <> +struct ObjectTraits<wp_viewport> { + static const wl_interface* interface; + static void (*deleter)(wp_viewport*); +}; + +template <> +struct ObjectTraits<wp_viewporter> { + static const wl_interface* interface; + static void (*deleter)(wp_viewporter*); +}; + +template <> struct ObjectTraits<xdg_wm_base> { static const wl_interface* interface; static void (*deleter)(xdg_wm_base*); @@ -280,6 +314,24 @@ struct ObjectTraits<zwp_linux_dmabuf_v1> { }; template <> +struct ObjectTraits<zwp_linux_buffer_release_v1> { + static const wl_interface* interface; + static void (*deleter)(zwp_linux_buffer_release_v1*); +}; + +template <> +struct ObjectTraits<zwp_linux_explicit_synchronization_v1> { + static const wl_interface* interface; + static void (*deleter)(zwp_linux_explicit_synchronization_v1*); +}; + +template <> +struct ObjectTraits<zwp_linux_surface_synchronization_v1> { + static const wl_interface* interface; + static void (*deleter)(zwp_linux_surface_synchronization_v1*); +}; + +template <> struct ObjectTraits<zxdg_shell_v6> { static const wl_interface* interface; static void (*deleter)(zxdg_shell_v6*); @@ -321,6 +373,18 @@ struct ObjectTraits<zwp_text_input_v1> { static void (*deleter)(zwp_text_input_v1*); }; +template <> +struct ObjectTraits<zxdg_exporter_v1> { + static const wl_interface* interface; + static void (*deleter)(zxdg_exporter_v1*); +}; + +template <> +struct ObjectTraits<zxdg_exported_v1> { + static const wl_interface* interface; + static void (*deleter)(zxdg_exported_v1*); +}; + struct Deleter { template <typename T> void operator()(T* obj) { @@ -343,7 +407,7 @@ class Object : public std::unique_ptr<T, Deleter> { template <typename T> wl::Object<T> Bind(wl_registry* registry, uint32_t name, uint32_t version) { return wl::Object<T>(static_cast<T*>( - wl_registry_bind(registry, name, ObjectTraits<T>::interface, version))); + wl::bind_registry(registry, name, ObjectTraits<T>::interface, version))); } } // namespace wl diff --git a/chromium/ui/ozone/platform/wayland/common/wayland_util.cc b/chromium/ui/ozone/platform/wayland/common/wayland_util.cc index 3b0b8829a95..042cfc8e96b 100644 --- a/chromium/ui/ozone/platform/wayland/common/wayland_util.cc +++ b/chromium/ui/ozone/platform/wayland/common/wayland_util.cc @@ -157,6 +157,98 @@ gfx::Rect TranslateBoundsToTopLevelCoordinates(const gfx::Rect& child_bounds, child_bounds.size()); } +wl_output_transform ToWaylandTransform(gfx::OverlayTransform transform) { + switch (transform) { + case gfx::OVERLAY_TRANSFORM_NONE: + return WL_OUTPUT_TRANSFORM_NORMAL; + case gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL: + return WL_OUTPUT_TRANSFORM_FLIPPED; + case gfx::OVERLAY_TRANSFORM_FLIP_VERTICAL: + return WL_OUTPUT_TRANSFORM_FLIPPED_180; + case gfx::OVERLAY_TRANSFORM_ROTATE_90: + return WL_OUTPUT_TRANSFORM_90; + case gfx::OVERLAY_TRANSFORM_ROTATE_180: + return WL_OUTPUT_TRANSFORM_180; + case gfx::OVERLAY_TRANSFORM_ROTATE_270: + return WL_OUTPUT_TRANSFORM_270; + default: + break; + } + NOTREACHED(); + return WL_OUTPUT_TRANSFORM_NORMAL; +} + +gfx::Rect ApplyWaylandTransform(const gfx::Rect& rect, + const gfx::Size& bounds, + wl_output_transform transform) { + gfx::Rect result = rect; + switch (transform) { + case WL_OUTPUT_TRANSFORM_NORMAL: + break; + case WL_OUTPUT_TRANSFORM_FLIPPED: + result.set_x(bounds.width() - rect.x() - rect.width()); + break; + case WL_OUTPUT_TRANSFORM_FLIPPED_90: + result.set_x(rect.y()); + result.set_y(rect.x()); + result.set_width(rect.height()); + result.set_height(rect.width()); + break; + case WL_OUTPUT_TRANSFORM_FLIPPED_180: + result.set_y(bounds.height() - rect.y() - rect.height()); + break; + case WL_OUTPUT_TRANSFORM_FLIPPED_270: + result.set_x(bounds.height() - rect.y() - rect.height()); + result.set_y(bounds.width() - rect.x() - rect.width()); + result.set_width(rect.height()); + result.set_height(rect.width()); + break; + case WL_OUTPUT_TRANSFORM_90: + result.set_x(rect.y()); + result.set_y(bounds.width() - rect.x() - rect.width()); + result.set_width(rect.height()); + result.set_height(rect.width()); + break; + case WL_OUTPUT_TRANSFORM_180: + result.set_x(bounds.width() - rect.x() - rect.width()); + result.set_y(bounds.height() - rect.y() - rect.height()); + break; + case WL_OUTPUT_TRANSFORM_270: + result.set_x(bounds.height() - rect.y() - rect.height()); + result.set_y(rect.x()); + result.set_width(rect.height()); + result.set_height(rect.width()); + break; + default: + NOTREACHED(); + break; + } + return result; +} + +gfx::Size ApplyWaylandTransform(const gfx::Size& size, + wl_output_transform transform) { + gfx::Size result = size; + switch (transform) { + case WL_OUTPUT_TRANSFORM_NORMAL: + case WL_OUTPUT_TRANSFORM_FLIPPED: + case WL_OUTPUT_TRANSFORM_FLIPPED_180: + case WL_OUTPUT_TRANSFORM_180: + break; + case WL_OUTPUT_TRANSFORM_FLIPPED_90: + case WL_OUTPUT_TRANSFORM_FLIPPED_270: + case WL_OUTPUT_TRANSFORM_90: + case WL_OUTPUT_TRANSFORM_270: + result.set_width(size.height()); + result.set_height(size.width()); + break; + default: + NOTREACHED(); + break; + } + return result; +} + bool IsMenuType(ui::PlatformWindowType type) { return type == ui::PlatformWindowType::kMenu || type == ui::PlatformWindowType::kPopup; diff --git a/chromium/ui/ozone/platform/wayland/common/wayland_util.h b/chromium/ui/ozone/platform/wayland/common/wayland_util.h index 582a3c7b0ba..d755de9e0ba 100644 --- a/chromium/ui/ozone/platform/wayland/common/wayland_util.h +++ b/chromium/ui/ozone/platform/wayland/common/wayland_util.h @@ -7,13 +7,12 @@ #include <vector> -#include <wayland-client.h> - #include "base/callback.h" #include "base/containers/flat_map.h" #include "base/files/scoped_file.h" #include "base/macros.h" #include "ui/gfx/geometry/rect.h" +#include "ui/gfx/overlay_transform.h" #include "ui/ozone/platform/wayland/common/wayland_object.h" #include "ui/platform_window/platform_window_init_properties.h" @@ -60,6 +59,21 @@ gfx::Rect TranslateBoundsToParentCoordinates(const gfx::Rect& child_bounds, gfx::Rect TranslateBoundsToTopLevelCoordinates(const gfx::Rect& child_bounds, const gfx::Rect& parent_bounds); +// Returns wl_output_transform corresponding |transform|. |transform| is an +// enumeration of a fixed selection of transformations. +wl_output_transform ToWaylandTransform(gfx::OverlayTransform transform); + +// |bounds| contains |rect|. ApplyWaylandTransform() returns the resulted +// |rect| after transformation is applied to |bounds| containing |rect| as a +// whole. +gfx::Rect ApplyWaylandTransform(const gfx::Rect& rect, + const gfx::Size& bounds, + wl_output_transform transform); + +// Applies transformation to |size|. +gfx::Size ApplyWaylandTransform(const gfx::Size& size, + wl_output_transform transform); + // Says if the type is kPopup or kMenu. bool IsMenuType(ui::PlatformWindowType type); diff --git a/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc b/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc index 8ee69e5b401..b77c13b5e75 100644 --- a/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc +++ b/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc @@ -46,8 +46,10 @@ GbmSurfacelessWayland::GbmSurfacelessWayland( } void GbmSurfacelessWayland::QueueOverlayPlane(OverlayPlane plane, - uint32_t buffer_id) { - unsubmitted_frames_.back()->planes.push_back({std::move(plane), buffer_id}); + BufferId buffer_id) { + auto result = + unsubmitted_frames_.back()->planes.emplace(buffer_id, std::move(plane)); + DCHECK(result.second); } bool GbmSurfacelessWayland::ScheduleOverlayPlane( @@ -100,7 +102,7 @@ void GbmSurfacelessWayland::SwapBuffersAsync( return; } - // TODO(dcastagna): remove glFlush since eglImageFlushExternalEXT called on + // TODO(fangzhoug): remove glFlush since eglImageFlushExternalEXT called on // the image should be enough (https://crbug.com/720045). if (!no_gl_flush_for_tests_) glFlush(); @@ -123,16 +125,16 @@ void GbmSurfacelessWayland::SwapBuffersAsync( // Uset in-fences provided in the overlays. If there are none, we insert our // own fence and wait. for (auto& plane : frame->planes) { - if (plane.plane.gpu_fence) - fences.push_back(std::move(plane.plane.gpu_fence)); + if (plane.second.gpu_fence) + fences.push_back(std::move(plane.second.gpu_fence)); } base::OnceClosure fence_wait_task; if (!fences.empty()) { fence_wait_task = base::BindOnce(&WaitForGpuFences, std::move(fences)); } else { - // TODO: the following should be replaced by a per surface flush as it gets - // implemented in GL drivers. + // TODO(fangzhoug): the following should be replaced by a per surface flush + // as it gets implemented in GL drivers. EGLSyncKHR fence = InsertFence(has_implicit_external_sync_); CHECK_NE(fence, EGL_NO_SYNC_KHR) << "eglCreateSyncKHR failed"; @@ -238,18 +240,14 @@ void GbmSurfacelessWayland::MaybeSubmitFrames() { std::vector<ui::ozone::mojom::WaylandOverlayConfigPtr> overlay_configs; for (const auto& plane : submitted_frame->planes) { overlay_configs.push_back( - ui::ozone::mojom::WaylandOverlayConfig::From(plane.plane)); - overlay_configs.back()->buffer_id = plane.buffer_id; - if (plane.plane.z_order == 0) { + ui::ozone::mojom::WaylandOverlayConfig::From(plane.second)); + overlay_configs.back()->buffer_id = plane.first; + if (plane.second.z_order == 0) { overlay_configs.back()->damage_region = submitted_frame->damage_region_; - submitted_frame->buffer_id = plane.buffer_id; + submitted_frame->buffer_id = plane.first; } } buffer_manager_->CommitOverlays(widget_, std::move(overlay_configs)); - - submitted_frame->unacked_submissions = submitted_frame->planes.size(); - submitted_frame->unacked_presentations = submitted_frame->planes.size(); - submitted_frame->planes.clear(); submitted_frames_.push_back(std::move(submitted_frame)); } } @@ -271,22 +269,46 @@ void GbmSurfacelessWayland::SetNoGLFlushForTests() { no_gl_flush_for_tests_ = true; } -void GbmSurfacelessWayland::OnSubmission(uint32_t buffer_id, +void GbmSurfacelessWayland::OnSubmission(BufferId buffer_id, const gfx::SwapResult& swap_result) { // submitted_frames_ may temporarily have more than one buffer in it if // buffers are released out of order by the Wayland server. DCHECK(!submitted_frames_.empty()); - if (--submitted_frames_.front()->unacked_submissions) - return; - - auto submitted_frame = std::move(submitted_frames_.front()); - submitted_frames_.erase(submitted_frames_.begin()); - submitted_frame->overlays.clear(); - std::move(submitted_frame->completion_callback) - .Run(gfx::SwapCompletionResult(swap_result)); - - pending_presentation_frames_.push_back(std::move(submitted_frame)); + size_t erased = 0; + for (auto& submitted_frame : submitted_frames_) { + if ((erased = submitted_frame->planes.erase(buffer_id)) > 0) { + // |completion_callback| only takes 1 SwapResult. It's possible that only + // one of the buffers in a frame gets a SWAP_FAILED or + // SWAP_NAK_RECREATE_BUFFERS. Don't replace a failed swap_result with + // SWAP_ACK. If both SWAP_FAILED and SWAP_NAK_RECREATE_BUFFERS happens, + // this swap is treated as SWAP_FAILED. + if (submitted_frame->swap_result == gfx::SwapResult::SWAP_ACK || + swap_result == gfx::SwapResult::SWAP_FAILED) { + submitted_frame->swap_result = swap_result; + } + submitted_frame->pending_presentation_buffers.insert(buffer_id); + break; + } + } + DCHECK(erased); + + // Following while loop covers below scenario: + // frame_1 submitted a buffer_1 for overlay; frame_2 submitted a buffer_2 + // for primary plane. This can happen at the end of a single-on-top overlay. + // buffer_1 is not attached immediately due to unack'ed wl_frame_callback. + // buffer_2 is attached immediately Onsubmission() of buffer_2 runs. + while (!submitted_frames_.empty() && + submitted_frames_.front()->planes.empty()) { + auto submitted_frame = std::move(submitted_frames_.front()); + submitted_frames_.erase(submitted_frames_.begin()); + submitted_frame->overlays.clear(); + + std::move(submitted_frame->completion_callback) + .Run(gfx::SwapCompletionResult(submitted_frame->swap_result)); + + pending_presentation_frames_.push_back(std::move(submitted_frame)); + } if (swap_result != gfx::SwapResult::SWAP_ACK) { last_swap_buffers_result_ = false; @@ -297,10 +319,20 @@ void GbmSurfacelessWayland::OnSubmission(uint32_t buffer_id, } void GbmSurfacelessWayland::OnPresentation( - uint32_t buffer_id, + BufferId buffer_id, const gfx::PresentationFeedback& feedback) { + DCHECK(!submitted_frames_.empty() || !pending_presentation_frames_.empty()); + + size_t erased = 0; + for (auto& frame : pending_presentation_frames_) { + if ((erased = frame->pending_presentation_buffers.erase(buffer_id)) > 0) { + frame->feedback = feedback; + break; + } + } + // Items in |submitted_frames_| will not be moved to - // |pending_presentation_frames_| until |unacked_submissions| decrements to 0. + // |pending_presentation_frames_| until |planes| is empty. // Example: // A SwapBuffers that submitted 2 buffers (buffer_1 and buffer_2) will push // a submitted_frame expecting 2 submission feedbacks and 2 presentation @@ -312,24 +344,26 @@ void GbmSurfacelessWayland::OnPresentation( // buffer_1:submission > buffer_1:presentation > buffer_2:submission > // buffer_2:presentation // In this case, we have to find the item in |submitted_frames_| and - // decrement |unacked_presentations| there. - // TODO(fangzhoug): This solution is sub-optimal and confusing. It increases - // the number of IPCs from browser to gpu. The barrier logic should be in the - // browser process. - if (pending_presentation_frames_.empty()) { - auto it = submitted_frames_.begin(); - for (; !(*it)->unacked_presentations; ++it) - ; - --(*it)->unacked_presentations; - return; + // remove from |pending_presentation_buffers| there. + if (!erased) { + for (auto& frame : submitted_frames_) { + if ((erased = frame->pending_presentation_buffers.erase(buffer_id)) > 0) { + frame->feedback = feedback; + break; + } + } } - auto* frame = pending_presentation_frames_.front().get(); - if (--frame->unacked_presentations) - return; + DCHECK(erased); - std::move(frame->presentation_callback).Run(feedback); - pending_presentation_frames_.erase(pending_presentation_frames_.begin()); + while (!pending_presentation_frames_.empty() && + pending_presentation_frames_.front() + ->pending_presentation_buffers.empty()) { + auto* frame = pending_presentation_frames_.front().get(); + DCHECK(frame->planes.empty()); + std::move(frame->presentation_callback).Run(frame->feedback); + pending_presentation_frames_.erase(pending_presentation_frames_.begin()); + } } } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h b/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h index 13dbd84ab90..910b9a7056e 100644 --- a/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h +++ b/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h @@ -7,6 +7,7 @@ #include <memory> +#include "base/containers/small_map.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "ui/gfx/native_widget_types.h" @@ -19,6 +20,8 @@ namespace ui { class WaylandBufferManagerGpu; +using BufferId = uint32_t; + // A GLSurface for Wayland Ozone platform that uses surfaceless drawing. Drawing // and displaying happens directly through NativePixmap buffers. CC would call // into SurfaceFactoryOzone to allocate the buffers and then call @@ -29,7 +32,7 @@ class GbmSurfacelessWayland : public gl::SurfacelessEGL, GbmSurfacelessWayland(WaylandBufferManagerGpu* buffer_manager, gfx::AcceleratedWidget widget); - void QueueOverlayPlane(OverlayPlane plane, uint32_t buffer_id); + void QueueOverlayPlane(OverlayPlane plane, BufferId buffer_id); // gl::GLSurface: bool ScheduleOverlayPlane(int z_order, @@ -62,22 +65,19 @@ class GbmSurfacelessWayland : public gl::SurfacelessEGL, private: FRIEND_TEST_ALL_PREFIXES(WaylandSurfaceFactoryTest, GbmSurfacelessWaylandCheckOrderOfCallbacksTest); + FRIEND_TEST_ALL_PREFIXES(WaylandSurfaceFactoryTest, + GbmSurfacelessWaylandCommitOverlaysCallbacksTest); + FRIEND_TEST_ALL_PREFIXES(WaylandSurfaceFactoryTest, + GbmSurfacelessWaylandGroupOnSubmissionCallbacksTest); ~GbmSurfacelessWayland() override; // WaylandSurfaceGpu overrides: - void OnSubmission(uint32_t buffer_id, + void OnSubmission(BufferId buffer_id, const gfx::SwapResult& swap_result) override; - void OnPresentation(uint32_t buffer_id, + void OnPresentation(BufferId buffer_id, const gfx::PresentationFeedback& feedback) override; - struct PlaneData { - OverlayPlane plane; - // The id of the buffer, which represents buffer that backs this overlay - // plane. - const uint32_t buffer_id; - }; - struct PendingFrame { PendingFrame(); ~PendingFrame(); @@ -89,24 +89,25 @@ class GbmSurfacelessWayland : public gl::SurfacelessEGL, bool ready = false; // The id of the buffer, which represents this frame. - uint32_t buffer_id = 0; + BufferId buffer_id = 0; // A region of the updated content in a corresponding frame. It's used to // advice Wayland which part of a buffer is going to be updated. Passing {0, // 0, 0, 0} results in a whole buffer update on the Wayland compositor side. gfx::Rect damage_region_ = gfx::Rect(); + // TODO(fangzhoug): This should be changed to support Vulkan. std::vector<gl::GLSurfaceOverlay> overlays; SwapCompletionCallback completion_callback; PresentationCallback presentation_callback; bool schedule_planes_succeeded = false; - std::vector<PlaneData> planes; - // TODO(fangzhoug): This is a temporary solution to barrier swap/present - // acks of a frame that contains multiple buffer commits. Next step is to - // barrier in browser process to avoid extra IPC hops. - size_t unacked_submissions; - size_t unacked_presentations; + // Maps |buffer_id| to an OverlayPlane, used for committing overlays and + // wait for OnSubmission's. + base::small_map<std::map<BufferId, OverlayPlane>> planes; + base::flat_set<BufferId> pending_presentation_buffers; + gfx::SwapResult swap_result = gfx::SwapResult::SWAP_ACK; + gfx::PresentationFeedback feedback; }; void MaybeSubmitFrames(); diff --git a/chromium/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc b/chromium/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc index 42fd8192f00..9897b51fd91 100644 --- a/chromium/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc +++ b/chromium/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc @@ -28,9 +28,8 @@ TypeConverter<ui::ozone::mojom::WaylandOverlayConfigPtr, wayland_overlay_config->enable_blend = input.enable_blend; wayland_overlay_config->access_fence_handle = !input.gpu_fence || input.gpu_fence->GetGpuFenceHandle().is_null() - ? base::Optional<gfx::GpuFenceHandle>() - : base::Optional<gfx::GpuFenceHandle>( - gfx::CloneHandleForIPC(input.gpu_fence->GetGpuFenceHandle())); + ? gfx::GpuFenceHandle() + : input.gpu_fence->GetGpuFenceHandle().Clone(); return wayland_overlay_config; } diff --git a/chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory_unittest.cc b/chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory_unittest.cc index ebec5b49347..1c506265172 100644 --- a/chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory_unittest.cc +++ b/chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory_unittest.cc @@ -19,6 +19,7 @@ #include "ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h" #include "ui/ozone/platform/wayland/gpu/wayland_surface_factory.h" #include "ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h" +#include "ui/ozone/platform/wayland/host/wayland_subsurface.h" #include "ui/ozone/platform/wayland/host/wayland_window.h" #include "ui/ozone/platform/wayland/test/mock_surface.h" #include "ui/ozone/platform/wayland/test/scoped_wl_array.h" @@ -125,19 +126,23 @@ class CallbacksHelper { // that 1) the image has been sent to be shown after being scheduled 2) the // image is displayed. This sort of mimics a buffer queue, but in a simpliear // way. - void FinishSwapBuffersAsync(uint32_t local_swap_id, - scoped_refptr<FakeGLImageNativePixmap> gl_image, - gfx::SwapCompletionResult result) { + void FinishSwapBuffersAsync( + uint32_t local_swap_id, + std::vector<scoped_refptr<FakeGLImageNativePixmap>> gl_images, + gfx::SwapCompletionResult result) { last_finish_swap_id_ = pending_local_swap_ids_.front(); pending_local_swap_ids_.pop(); - EXPECT_EQ(gl_image->GetAssociateWithSwapId(), last_finish_swap_id_); - EXPECT_TRUE(gl_image->busy() && !gl_image->displayed()); - if (displayed_image_) - displayed_image_->SetDisplayed(false); - displayed_image_ = gl_image; - displayed_image_->SetBusy(false); - displayed_image_->SetDisplayed(true); + for (auto& gl_image : gl_images) { + EXPECT_EQ(gl_image->GetAssociateWithSwapId(), last_finish_swap_id_); + EXPECT_TRUE(gl_image->busy() && !gl_image->displayed()); + gl_image->SetBusy(false); + gl_image->SetDisplayed(true); + } + + for (auto& displayed_image : displayed_images_) + displayed_image->SetDisplayed(false); + displayed_images_ = std::move(gl_images); } void BufferPresented(uint64_t local_swap_id, @@ -156,7 +161,7 @@ class CallbacksHelper { base::queue<uint64_t> pending_local_swap_ids_; // Keeps track of a displayed image. - scoped_refptr<FakeGLImageNativePixmap> displayed_image_; + std::vector<scoped_refptr<FakeGLImageNativePixmap>> displayed_images_; }; } // namespace @@ -253,10 +258,13 @@ TEST_P(WaylandSurfaceFactoryTest, 0, gfx::OverlayTransform::OVERLAY_TRANSFORM_FLIP_VERTICAL, gl_image.get(), window_->GetBounds(), {}, false, nullptr); + std::vector<scoped_refptr<FakeGLImageNativePixmap>> gl_images; + gl_images.push_back(gl_image); + // And submit each image. They will be executed in FIFO manner. gl_surface->SwapBuffersAsync( base::BindOnce(&CallbacksHelper::FinishSwapBuffersAsync, - base::Unretained(&cbs_helper), swap_id, gl_image), + base::Unretained(&cbs_helper), swap_id, gl_images), base::BindOnce(&CallbacksHelper::BufferPresented, base::Unretained(&cbs_helper), swap_id)); } @@ -402,6 +410,473 @@ TEST_P(WaylandSurfaceFactoryTest, mock_surface->SendFrameCallback(); } +TEST_P(WaylandSurfaceFactoryTest, + GbmSurfacelessWaylandCommitOverlaysCallbacksTest) { + // GbmSurfacelessWaylandCheckOrderOfCallbacksTest tests with one buffer per + // frame. This tests multiple buffers per-frame and order of + // SwapCompletionCallbacks. Even when all OnSubmission from later frames are + // called, their SwapCompletionCallbacks should not run until previous frames' + // SwapCompletionCallbacks run. + gl::SetGLImplementation(gl::kGLImplementationEGLGLES2); + + buffer_manager_gpu_->set_gbm_device(std::make_unique<MockGbmDevice>()); + + auto* gl_ozone = surface_factory_->GetGLOzone(gl::kGLImplementationEGLGLES2); + auto gl_surface = gl_ozone->CreateSurfacelessViewGLSurface(widget_); + EXPECT_TRUE(gl_surface); + gl_surface->SetRelyOnImplicitSync(); + static_cast<ui::GbmSurfacelessWayland*>(gl_surface.get()) + ->SetNoGLFlushForTests(); + + // Expect to create 4 buffers. + EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(4); + + // Create buffers and FakeGlImageNativePixmap. + std::vector<scoped_refptr<FakeGLImageNativePixmap>> fake_gl_image; + for (int i = 0; i < 4; ++i) { + auto native_pixmap = surface_factory_->CreateNativePixmap( + widget_, nullptr, window_->GetBounds().size(), + gfx::BufferFormat::BGRA_8888, gfx::BufferUsage::SCANOUT); + fake_gl_image.push_back(base::MakeRefCounted<FakeGLImageNativePixmap>( + native_pixmap, window_->GetBounds().size())); + + Sync(); + + // Create one buffer at a time. + auto params_vector = server_.zwp_linux_dmabuf_v1()->buffer_params(); + DCHECK_EQ(params_vector.size(), 1u); + zwp_linux_buffer_params_v1_send_created( + params_vector.front()->resource(), + params_vector.front()->buffer_resource()); + + Sync(); + } + + auto* mock_primary_surface = server_.GetObject<wl::MockSurface>( + window_->root_surface()->GetSurfaceId()); + + CallbacksHelper cbs_helper; + // Submit a frame with only primary plane + { + // Associate each image with swap id so that we could track released + // buffers. + auto swap_id = cbs_helper.GetNextLocalSwapId(); + // Associate the image with the next swap id so that we can easily track if + // it became free to reuse. + fake_gl_image[0]->AssociateWithSwapId(swap_id); + // And set it to be busy... + fake_gl_image[0]->SetBusy(true); + + // Prepare overlay plane. + gl_surface->ScheduleOverlayPlane( + 0, gfx::OverlayTransform::OVERLAY_TRANSFORM_NONE, + fake_gl_image[0].get(), window_->GetBounds(), {}, false, nullptr); + + std::vector<scoped_refptr<FakeGLImageNativePixmap>> gl_images; + gl_images.push_back(fake_gl_image[0]); + + // And submit each image. They will be executed in FIFO manner. + gl_surface->SwapBuffersAsync( + base::BindOnce(&CallbacksHelper::FinishSwapBuffersAsync, + base::Unretained(&cbs_helper), swap_id, gl_images), + base::BindOnce(&CallbacksHelper::BufferPresented, + base::Unretained(&cbs_helper), swap_id)); + } + + // Let's sync so that 1) GbmSurfacelessWayland submits the buffer according to + // internal queue and fake server processes the request. + + // Also, we expect only one buffer to be committed. + EXPECT_CALL(*mock_primary_surface, Attach(_, _, _)).Times(1); + EXPECT_CALL(*mock_primary_surface, Frame(_)).Times(1); + EXPECT_CALL(*mock_primary_surface, DamageBuffer(_, _, _, _)).Times(1); + EXPECT_CALL(*mock_primary_surface, Commit()).Times(1); + + Sync(); + + testing::Mock::VerifyAndClearExpectations(&mock_primary_surface); + + // Give mojo the chance to pass the callbacks. + base::RunLoop().RunUntilIdle(); + + // We have just received Attach/DamageBuffer/Commit for buffer with swap + // id=0u. The SwapCompletionCallback must be executed automatically as long as + // we didn't have any buffers attached to the surface before. + EXPECT_EQ(cbs_helper.GetLastFinishedSwapId(), 0u); + + cbs_helper.ResetLastFinishedSwapId(); + + for (const auto& gl_image : fake_gl_image) { + // All the images except the first one, which was associated with swap + // id=0u, must be busy and not displayed. The first one must be displayed. + if (gl_image->GetAssociateWithSwapId() == 0u) { + EXPECT_FALSE(gl_image->busy()); + EXPECT_TRUE(gl_image->displayed()); + } else { + EXPECT_FALSE(gl_image->displayed()); + } + } + + // Submit another frame with only primary plane + { + // Associate each image with swap id so that we could track released + // buffers. + auto swap_id = cbs_helper.GetNextLocalSwapId(); + // Associate the image with the next swap id so that we can easily track if + // it became free to reuse. + fake_gl_image[1]->AssociateWithSwapId(swap_id); + // And set it to be busy... + fake_gl_image[1]->SetBusy(true); + + // Prepare overlay plane. + gl_surface->ScheduleOverlayPlane( + 0, gfx::OverlayTransform::OVERLAY_TRANSFORM_NONE, + fake_gl_image[1].get(), window_->GetBounds(), {}, false, nullptr); + + std::vector<scoped_refptr<FakeGLImageNativePixmap>> gl_images; + gl_images.push_back(fake_gl_image[1]); + + // And submit each image. They will be executed in FIFO manner. + gl_surface->SwapBuffersAsync( + base::BindOnce(&CallbacksHelper::FinishSwapBuffersAsync, + base::Unretained(&cbs_helper), swap_id, gl_images), + base::BindOnce(&CallbacksHelper::BufferPresented, + base::Unretained(&cbs_helper), swap_id)); + } + + // Expect one buffer to be committed. + EXPECT_CALL(*mock_primary_surface, Attach(_, _, _)).Times(1); + EXPECT_CALL(*mock_primary_surface, Frame(_)).Times(1); + EXPECT_CALL(*mock_primary_surface, DamageBuffer(_, _, _, _)).Times(1); + EXPECT_CALL(*mock_primary_surface, Commit()).Times(1); + + // Send the frame callback so that pending buffer for swap id=1u is processed + // and swapped. + mock_primary_surface->SendFrameCallback(); + + Sync(); + + testing::Mock::VerifyAndClearExpectations(&mock_primary_surface); + + // Give mojo the chance to pass the callbacks. + base::RunLoop().RunUntilIdle(); + + // Even though the second buffer was submitted, we mustn't receive + // SwapCompletionCallback until the previous buffer is released. + EXPECT_EQ(cbs_helper.GetLastFinishedSwapId(), + std::numeric_limits<uint32_t>::max()); + + // Submit another frame with 2 overlays, 0 primary plane. + { + // Associate each image with swap id so that we could track released + // buffers. + auto swap_id = cbs_helper.GetNextLocalSwapId(); + // Associate the image with the next swap id so that we can easily track if + // it became free to reuse. + fake_gl_image[2]->AssociateWithSwapId(swap_id); + // And set it to be busy... + fake_gl_image[2]->SetBusy(true); + + // Prepare overlay plane. + gl_surface->ScheduleOverlayPlane( + -1, gfx::OverlayTransform::OVERLAY_TRANSFORM_NONE, + fake_gl_image[2].get(), window_->GetBounds(), {}, false, nullptr); + + // Associate the image with the next swap id so that we can easily track if + // it became free to reuse. + fake_gl_image[3]->AssociateWithSwapId(swap_id); + // And set it to be busy... + fake_gl_image[3]->SetBusy(true); + + // Prepare overlay plane. + gl_surface->ScheduleOverlayPlane( + 1, gfx::OverlayTransform::OVERLAY_TRANSFORM_NONE, + fake_gl_image[3].get(), window_->GetBounds(), {}, false, nullptr); + + std::vector<scoped_refptr<FakeGLImageNativePixmap>> gl_images; + gl_images.push_back(fake_gl_image[2]); + gl_images.push_back(fake_gl_image[3]); + + // And submit each image. They will be executed in FIFO manner. + gl_surface->SwapBuffersAsync( + base::BindOnce(&CallbacksHelper::FinishSwapBuffersAsync, + base::Unretained(&cbs_helper), swap_id, gl_images), + base::BindOnce(&CallbacksHelper::BufferPresented, + base::Unretained(&cbs_helper), swap_id)); + } + // Expect parent surface to be committed without a buffer. + EXPECT_CALL(*mock_primary_surface, Attach(_, _, _)).Times(0); + EXPECT_CALL(*mock_primary_surface, Frame(_)).Times(1); + EXPECT_CALL(*mock_primary_surface, DamageBuffer(_, _, _, _)).Times(0); + EXPECT_CALL(*mock_primary_surface, Commit()).Times(1); + + // Send the frame callback so that pending buffer for swap id=2u is processed + // and swapped. + mock_primary_surface->SendFrameCallback(); + + Sync(); + + // Give mojo the chance to pass the callbacks. + base::RunLoop().RunUntilIdle(); + + // Even though OnSubmission can come back because 2 overlays are submitted to + // new wl_surfaces so OnSubmission for third frame has already run, + // GbmSurfacelessWayland should not run SwapCompletionCallback out of order. + EXPECT_EQ(cbs_helper.GetLastFinishedSwapId(), + std::numeric_limits<uint32_t>::max()); + + // This will result in Wayland server releasing previously attached buffer for + // swap id=1u and calling OnSubmission for buffer with swap id=1u. + mock_primary_surface->ReleaseBuffer( + mock_primary_surface->prev_attached_buffer()); + + Sync(); + + // Give mojo the chance to pass the callbacks. + base::RunLoop().RunUntilIdle(); + + // We should expect next 2 SwapCompletionCallbacks for the next 2 swap ids + // consecutively. + EXPECT_EQ(cbs_helper.GetLastFinishedSwapId(), 2u); + + cbs_helper.ResetLastFinishedSwapId(); + + for (const auto& gl_image : fake_gl_image) { + if (gl_image->GetAssociateWithSwapId() == 2u) { + EXPECT_TRUE(gl_image->displayed()); + EXPECT_FALSE(gl_image->busy()); + } else { + EXPECT_FALSE(gl_image->displayed()); + } + } +} + +TEST_P(WaylandSurfaceFactoryTest, + GbmSurfacelessWaylandGroupOnSubmissionCallbacksTest) { + // This tests multiple buffers per-frame. GbmSurfacelessWayland should wait + // for all OnSubmission calls targeting the same frame before running + // SwapCompletionCallbacks. + gl::SetGLImplementation(gl::kGLImplementationEGLGLES2); + + buffer_manager_gpu_->set_gbm_device(std::make_unique<MockGbmDevice>()); + + auto* gl_ozone = surface_factory_->GetGLOzone(gl::kGLImplementationEGLGLES2); + auto gl_surface = gl_ozone->CreateSurfacelessViewGLSurface(widget_); + EXPECT_TRUE(gl_surface); + gl_surface->SetRelyOnImplicitSync(); + static_cast<ui::GbmSurfacelessWayland*>(gl_surface.get()) + ->SetNoGLFlushForTests(); + + // Expect to create 4 buffers. + EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(4); + + // Create buffers and FakeGlImageNativePixmap. + std::vector<scoped_refptr<FakeGLImageNativePixmap>> fake_gl_image; + for (int i = 0; i < 4; ++i) { + auto native_pixmap = surface_factory_->CreateNativePixmap( + widget_, nullptr, window_->GetBounds().size(), + gfx::BufferFormat::BGRA_8888, gfx::BufferUsage::SCANOUT); + fake_gl_image.push_back(base::MakeRefCounted<FakeGLImageNativePixmap>( + native_pixmap, window_->GetBounds().size())); + + Sync(); + + // Create one buffer at a time. + auto params_vector = server_.zwp_linux_dmabuf_v1()->buffer_params(); + DCHECK_EQ(params_vector.size(), 1u); + zwp_linux_buffer_params_v1_send_created( + params_vector.front()->resource(), + params_vector.front()->buffer_resource()); + + Sync(); + } + + auto* mock_primary_surface = server_.GetObject<wl::MockSurface>( + window_->root_surface()->GetSurfaceId()); + + CallbacksHelper cbs_helper; + // Submit a frame with 1 primary plane and 1 overlay + { + // Associate each image with swap id so that we could track released + // buffers. + auto swap_id = cbs_helper.GetNextLocalSwapId(); + // Associate the image with the next swap id so that we can easily track if + // it became free to reuse. + fake_gl_image[0]->AssociateWithSwapId(swap_id); + // And set it to be busy... + fake_gl_image[0]->SetBusy(true); + + // Prepare overlay plane. + gl_surface->ScheduleOverlayPlane( + 0, gfx::OverlayTransform::OVERLAY_TRANSFORM_NONE, + fake_gl_image[0].get(), window_->GetBounds(), {}, false, nullptr); + + // Associate the image with the next swap id so that we can easily track if + // it became free to reuse. + fake_gl_image[1]->AssociateWithSwapId(swap_id); + // And set it to be busy... + fake_gl_image[1]->SetBusy(true); + + // Prepare overlay plane. + gl_surface->ScheduleOverlayPlane( + 1, gfx::OverlayTransform::OVERLAY_TRANSFORM_NONE, + fake_gl_image[1].get(), window_->GetBounds(), {}, false, nullptr); + + std::vector<scoped_refptr<FakeGLImageNativePixmap>> gl_images; + gl_images.push_back(fake_gl_image[0]); + gl_images.push_back(fake_gl_image[1]); + + // And submit each image. They will be executed in FIFO manner. + gl_surface->SwapBuffersAsync( + base::BindOnce(&CallbacksHelper::FinishSwapBuffersAsync, + base::Unretained(&cbs_helper), swap_id, gl_images), + base::BindOnce(&CallbacksHelper::BufferPresented, + base::Unretained(&cbs_helper), swap_id)); + } + + // Let's sync so that 1) GbmSurfacelessWayland submits the buffer according to + // internal queue and fake server processes the request. + + // Also, we expect only one buffer to be committed. + EXPECT_CALL(*mock_primary_surface, Attach(_, _, _)).Times(1); + EXPECT_CALL(*mock_primary_surface, Frame(_)).Times(1); + EXPECT_CALL(*mock_primary_surface, DamageBuffer(_, _, _, _)).Times(1); + EXPECT_CALL(*mock_primary_surface, Commit()).Times(1); + + Sync(); + + testing::Mock::VerifyAndClearExpectations(&mock_primary_surface); + auto* subsurface = window_->wayland_subsurfaces().begin()->get(); + auto* mock_overlay_surface = server_.GetObject<wl::MockSurface>( + subsurface->wayland_surface()->GetSurfaceId()); + + // Give mojo the chance to pass the callbacks. + base::RunLoop().RunUntilIdle(); + + // We have just received Attach/DamageBuffer/Commit for buffer with swap + // id=0u. The SwapCompletionCallback must be executed automatically as long as + // we didn't have any buffers attached to the surface before. + EXPECT_EQ(cbs_helper.GetLastFinishedSwapId(), 0u); + + cbs_helper.ResetLastFinishedSwapId(); + + for (const auto& gl_image : fake_gl_image) { + // All the images except the first one, which was associated with swap + // id=0u, must be busy and not displayed. The first one must be displayed. + if (gl_image->GetAssociateWithSwapId() == 0u) { + EXPECT_FALSE(gl_image->busy()); + EXPECT_TRUE(gl_image->displayed()); + } else { + EXPECT_FALSE(gl_image->displayed()); + } + } + + // Submit another frame with 1 primary plane and 1 overlay + { + // Associate each image with swap id so that we could track released + // buffers. + auto swap_id = cbs_helper.GetNextLocalSwapId(); + // Associate the image with the next swap id so that we can easily track if + // it became free to reuse. + fake_gl_image[2]->AssociateWithSwapId(swap_id); + // And set it to be busy... + fake_gl_image[2]->SetBusy(true); + + // Prepare overlay plane. + gl_surface->ScheduleOverlayPlane( + 0, gfx::OverlayTransform::OVERLAY_TRANSFORM_NONE, + fake_gl_image[2].get(), window_->GetBounds(), {}, false, nullptr); + + // Associate the image with the next swap id so that we can easily track if + // it became free to reuse. + fake_gl_image[3]->AssociateWithSwapId(swap_id); + // And set it to be busy... + fake_gl_image[3]->SetBusy(true); + + // Prepare overlay plane. + gl_surface->ScheduleOverlayPlane( + 1, gfx::OverlayTransform::OVERLAY_TRANSFORM_NONE, + fake_gl_image[3].get(), window_->GetBounds(), {}, false, nullptr); + + std::vector<scoped_refptr<FakeGLImageNativePixmap>> gl_images; + gl_images.push_back(fake_gl_image[2]); + gl_images.push_back(fake_gl_image[3]); + + // And submit each image. They will be executed in FIFO manner. + gl_surface->SwapBuffersAsync( + base::BindOnce(&CallbacksHelper::FinishSwapBuffersAsync, + base::Unretained(&cbs_helper), swap_id, gl_images), + base::BindOnce(&CallbacksHelper::BufferPresented, + base::Unretained(&cbs_helper), swap_id)); + } + + // Expect one buffer to be committed. + EXPECT_CALL(*mock_primary_surface, Attach(_, _, _)).Times(1); + EXPECT_CALL(*mock_primary_surface, Frame(_)).Times(1); + EXPECT_CALL(*mock_primary_surface, DamageBuffer(_, _, _, _)).Times(1); + EXPECT_CALL(*mock_primary_surface, Commit()).Times(1); + + // Expect one buffer to be committed. + EXPECT_CALL(*mock_overlay_surface, Attach(_, _, _)).Times(1); + EXPECT_CALL(*mock_overlay_surface, Frame(_)).Times(0); + EXPECT_CALL(*mock_overlay_surface, DamageBuffer(_, _, _, _)).Times(1); + EXPECT_CALL(*mock_overlay_surface, Commit()).Times(1); + + // Send the frame callback so that pending buffer for swap id=1u is processed + // and swapped. + mock_primary_surface->SendFrameCallback(); + + Sync(); + + testing::Mock::VerifyAndClearExpectations(&mock_primary_surface); + + // Give mojo the chance to pass the callbacks. + base::RunLoop().RunUntilIdle(); + + // Even though the second frame was submitted, we mustn't receive + // SwapCompletionCallback until the previous frame's buffers are released. + EXPECT_EQ(cbs_helper.GetLastFinishedSwapId(), + std::numeric_limits<uint32_t>::max()); + + // This will result in Wayland server releasing one of the previously attached + // buffers for swap id=0u and calling OnSubmission for a buffer with swap + // id=1u attached to the primary surface. + mock_primary_surface->ReleaseBuffer( + mock_primary_surface->prev_attached_buffer()); + + Sync(); + + // Give mojo the chance to pass the callbacks. + base::RunLoop().RunUntilIdle(); + + // OnSubmission was only called for one of the buffers with swap id=1u, so + // SwapCompletionCallback should not run. + EXPECT_EQ(cbs_helper.GetLastFinishedSwapId(), + std::numeric_limits<uint32_t>::max()); + + // Release the another buffer. + mock_overlay_surface->ReleaseBuffer( + mock_overlay_surface->prev_attached_buffer()); + + Sync(); + + // Give mojo the chance to pass the callbacks. + base::RunLoop().RunUntilIdle(); + + // OnSubmission was called for both of the buffers with swap id=1u, so + // SwapCompletionCallback should run. + EXPECT_EQ(cbs_helper.GetLastFinishedSwapId(), 1u); + + for (const auto& gl_image : fake_gl_image) { + if (gl_image->GetAssociateWithSwapId() == 1u) { + EXPECT_TRUE(gl_image->displayed()); + EXPECT_FALSE(gl_image->busy()); + } else { + EXPECT_FALSE(gl_image->displayed()); + } + } +} + TEST_P(WaylandSurfaceFactoryTest, Canvas) { auto canvas = CreateCanvas(widget_); ASSERT_TRUE(canvas); diff --git a/chromium/ui/ozone/platform/wayland/host/DEPS b/chromium/ui/ozone/platform/wayland/host/DEPS new file mode 100644 index 00000000000..1741810f122 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/host/DEPS @@ -0,0 +1,4 @@ +include_rules = [ + # For Lacros. + "+chromeos/crosapi/cpp/crosapi_constants.h", +] diff --git a/chromium/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.cc b/chromium/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.cc index e5017840921..0a0e4712910 100644 --- a/chromium/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.cc +++ b/chromium/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.cc @@ -7,9 +7,21 @@ #include <gdk/gdkwayland.h> #include <gtk/gtk.h> +#include <memory> + +#include "base/bind.h" #include "base/logging.h" #include "ui/gfx/native_widget_types.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" +#include "ui/ozone/platform/wayland/host/wayland_surface.h" +#include "ui/ozone/platform/wayland/host/wayland_window.h" +#include "ui/ozone/platform/wayland/host/wayland_window_manager.h" +#include "ui/ozone/platform/wayland/host/xdg_foreign_wrapper.h" +#include "ui/platform_window/platform_window_init_properties.h" + +#define WEAK_GTK_FN(x) extern "C" __attribute__((weak)) decltype(x) x + +WEAK_GTK_FN(gdk_wayland_window_set_transient_for_exported); namespace ui { @@ -39,12 +51,29 @@ GdkWindow* GtkUiDelegateWayland::GetGdkWindow( bool GtkUiDelegateWayland::SetGdkWindowTransientFor( GdkWindow* window, gfx::AcceleratedWidget parent) { - NOTIMPLEMENTED_LOG_ONCE(); - return false; + if (!gdk_wayland_window_set_transient_for_exported) { + LOG(WARNING) << "set_transient_for_exported not supported in GTK version " + << GTK_MAJOR_VERSION << '.' << GTK_MINOR_VERSION << '.' + << GTK_MICRO_VERSION; + return false; + } + + auto* parent_window = + connection_->wayland_window_manager()->GetWindow(parent); + auto* foreign = connection_->xdg_foreign(); + if (!parent_window || !foreign) + return false; + + DCHECK_EQ(parent_window->type(), PlatformWindowType::kWindow); + + foreign->ExportSurfaceToForeign( + parent_window, base::BindOnce(&GtkUiDelegateWayland::OnHandle, + weak_factory_.GetWeakPtr(), window)); + return true; } void GtkUiDelegateWayland::ClearTransientFor(gfx::AcceleratedWidget parent) { - NOTIMPLEMENTED_LOG_ONCE(); + // Nothing to do here. } void GtkUiDelegateWayland::ShowGtkWindow(GtkWindow* window) { @@ -53,4 +82,10 @@ void GtkUiDelegateWayland::ShowGtkWindow(GtkWindow* window) { gtk_window_present(window); } +void GtkUiDelegateWayland::OnHandle(GdkWindow* window, + const std::string& handle) { + gdk_wayland_window_set_transient_for_exported( + window, const_cast<char*>(handle.c_str())); +} + } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.h b/chromium/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.h index 4f3f606c5ac..e5e23aeddac 100644 --- a/chromium/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.h +++ b/chromium/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.h @@ -5,6 +5,9 @@ #ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_GTK_UI_DELEGATE_WAYLAND_H_ #define UI_OZONE_PLATFORM_WAYLAND_HOST_GTK_UI_DELEGATE_WAYLAND_H_ +#include <string> + +#include "base/memory/weak_ptr.h" #include "ui/gfx/native_widget_types.h" #include "ui/gtk/gtk_ui_delegate.h" @@ -29,7 +32,13 @@ class GtkUiDelegateWayland : public GtkUiDelegate { void ShowGtkWindow(GtkWindow* window) override; private: + // Called when xdg-foreign exports a parent window passed in + // SetGdkWindowTransientFor. + void OnHandle(GdkWindow* window, const std::string& handle); + WaylandConnection* const connection_; + + base::WeakPtrFactory<GtkUiDelegateWayland> weak_factory_{this}; }; } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_auxiliary_window.cc b/chromium/ui/ozone/platform/wayland/host/wayland_auxiliary_window.cc index c5e31fd5b9d..50cbee6cad2 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_auxiliary_window.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_auxiliary_window.cc @@ -102,13 +102,16 @@ void WaylandAuxiliaryWindow::CreateSubsurface() { bool WaylandAuxiliaryWindow::OnInitialize( PlatformWindowInitProperties properties) { + DCHECK(!parent_window()); + // If we do not have parent window provided, we must always use a focused // window or a window that entered drag whenever the subsurface is created. - if (properties.parent_widget == gfx::kNullAcceleratedWidget) { - DCHECK(!parent_window()); + if (properties.parent_widget == gfx::kNullAcceleratedWidget) return true; - } - set_parent_window(GetParentWindow(properties.parent_widget)); + + set_parent_window( + connection()->wayland_window_manager()->FindParentForNewWindow( + properties.parent_widget)); return true; } diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc b/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc index c60c35e4ab4..5fa747f3301 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc @@ -25,6 +25,9 @@ namespace ui { namespace { +// Use |kInvalidBufferId| to commit surface state without updating wl_buffer. +constexpr uint32_t kInvalidBufferId = 0u; + uint32_t GetPresentationKindFlags(uint32_t flags) { // Wayland spec has different meaning of VSync. In Chromium, VSync means to // update the begin frame vsync timing based on presentation feedback. @@ -65,11 +68,20 @@ class WaylandBufferManagerHost::Surface { buffer_manager_(buffer_manager) {} ~Surface() = default; - bool CommitBuffer(uint32_t buffer_id, const gfx::Rect& damage_region) { + bool CommitBuffer(uint32_t buffer_id, + const gfx::Rect& damage_region, + bool wait_for_frame_callback) { // The window has already been destroyed. if (!wayland_surface_) return true; + // This is a buffer-less commit, do not lookup buffers. + if (buffer_id == kInvalidBufferId) { + pending_commits_.push_back({nullptr, wait_for_frame_callback}); + MaybeProcessPendingBuffer(); + return true; + } + WaylandBuffer* buffer = GetBuffer(buffer_id); if (!buffer) { // Get the anonymous_wl_buffer aka the buffer that has not been attached @@ -95,17 +107,11 @@ class WaylandBufferManagerHost::Surface { if (buffer->attached && !buffer->wl_buffer) return false; - pending_buffers_.push_back(buffer); + pending_commits_.push_back({buffer, wait_for_frame_callback}); MaybeProcessPendingBuffer(); return true; } - bool CreateBuffer(const gfx::Size& size, uint32_t buffer_id) { - auto result = buffers_.emplace( - buffer_id, std::make_unique<WaylandBuffer>(size, buffer_id)); - return result.second; - } - size_t DestroyBuffer(uint32_t buffer_id) { auto* buffer = GetBuffer(buffer_id); @@ -114,7 +120,11 @@ class WaylandBufferManagerHost::Surface { if (buffer) { buffer->released = true; MaybeProcessSubmittedBuffers(); - base::Erase(pending_buffers_, buffer); + for (auto it = pending_commits_.begin(); it != pending_commits_.end(); + ++it) { + if (it->buffer == buffer) + pending_commits_.erase(it++); + } } return buffers_.erase(buffer_id); @@ -145,7 +155,7 @@ class WaylandBufferManagerHost::Surface { ResetSurfaceContents(); submitted_buffers_.clear(); - pending_buffers_.clear(); + pending_commits_.clear(); connection_->ScheduleFlush(); } @@ -210,7 +220,16 @@ class WaylandBufferManagerHost::Surface { bool acked; }; - bool CommitBufferInternal(WaylandBuffer* buffer) { + // Represents a pending surface commit. + struct PendingCommit { + // If null, means this commit will not attach buffer. + WaylandBuffer* buffer = nullptr; + // Whether this commit must wait for a wl_frame_callback and setup another + // wl_frame_callback. + bool wait_for_callback = false; + }; + + bool CommitBufferInternal(WaylandBuffer* buffer, bool wait_for_callback) { DCHECK(buffer && wayland_surface_); // If the same buffer has been submitted again right after the client @@ -231,7 +250,8 @@ class WaylandBufferManagerHost::Surface { DamageBuffer(buffer); - SetupFrameCallback(); + if (wait_for_callback) + SetupFrameCallback(); SetupPresentationFeedback(buffer->buffer_id); CommitSurface(); @@ -252,7 +272,8 @@ class WaylandBufferManagerHost::Surface { pending_damage_region.set_size(buffer->size); DCHECK(!pending_damage_region.size().IsEmpty()); - wayland_surface_->Damage(pending_damage_region); + wayland_surface_->UpdateBufferDamageRegion(pending_damage_region, + buffer->size); } void AttachBuffer(WaylandBuffer* buffer) { @@ -509,10 +530,10 @@ class WaylandBufferManagerHost::Surface { } void MaybeProcessPendingBuffer() { - DCHECK_LE(pending_buffers_.size(), 6u); + DCHECK_LE(pending_commits_.size(), 6u); // There is nothing to process if there is no pending buffer or the window // has been destroyed. - if (pending_buffers_.empty() || !wayland_surface_) + if (pending_commits_.empty() || !wayland_surface_) return; // This request may come earlier than the Wayland compositor has imported a @@ -529,12 +550,27 @@ class WaylandBufferManagerHost::Surface { // // The third case happens if the window hasn't been configured until a // request to attach a buffer to its surface is sent. - auto* pending_buffer = pending_buffers_.front(); - if (!pending_buffer->wl_buffer || wl_frame_callback_ || !configured_) + auto pending_commit = std::move(pending_commits_.front()); + if ((pending_commit.buffer && !pending_commit.buffer->wl_buffer) || + (wl_frame_callback_ && pending_commit.wait_for_callback) || + !configured_) { + return; + } + + // A Commit without attaching buffers only needs to setup wl_frame_callback. + if (!pending_commit.buffer) { + pending_commits_.erase(pending_commits_.begin()); + if (pending_commit.wait_for_callback) + SetupFrameCallback(); + CommitSurface(); + connection_->ScheduleFlush(); + MaybeProcessSubmittedBuffers(); return; + } - pending_buffers_.erase(pending_buffers_.begin()); - CommitBufferInternal(pending_buffer); + pending_commits_.erase(pending_commits_.begin()); + CommitBufferInternal(pending_commit.buffer, + pending_commit.wait_for_callback); } // Widget this helper surface backs and has 1:1 relationship with the @@ -557,9 +593,9 @@ class WaylandBufferManagerHost::Surface { // operation. wl::Object<wl_callback> wl_frame_callback_; - // Queue of buffers which are pending to be submitted (look the comment + // Queue of commits which are pending to be submitted (look the comment // in the CommitBuffer method). - std::vector<WaylandBuffer*> pending_buffers_; + std::list<PendingCommit> pending_commits_; // Queue of buffers which have been submitted and are waiting to be // acked (send OnSubmission) @@ -639,6 +675,13 @@ void WaylandBufferManagerHost::OnSubsurfaceRemoved( surfaces_.erase(it); } +void WaylandBufferManagerHost::SetSurfaceConfigured(WaylandSurface* surface) { + DCHECK(surface); + auto it = surfaces_.find(surface); + DCHECK(it != surfaces_.end()); + it->second->OnSurfaceConfigured(); +} + void WaylandBufferManagerHost::SetTerminateGpuCallback( base::OnceCallback<void(std::string)> terminate_callback) { terminate_gpu_cb_ = std::move(terminate_callback); @@ -668,12 +711,13 @@ void WaylandBufferManagerHost::OnChannelDestroyed() { wl::BufferFormatsWithModifiersMap WaylandBufferManagerHost::GetSupportedBufferFormats() const { +#if defined(WAYLAND_GBM) if (connection_->zwp_dmabuf()) return connection_->zwp_dmabuf()->supported_buffer_formats(); else if (connection_->drm()) return connection_->drm()->supported_buffer_formats(); - else - return {}; +#endif + return {}; } bool WaylandBufferManagerHost::SupportsDmabuf() const { @@ -760,14 +804,16 @@ void WaylandBufferManagerHost::CreateShmBasedBuffer(mojo::PlatformHandle shm_fd, bool WaylandBufferManagerHost::CommitBufferInternal( WaylandSurface* wayland_surface, uint32_t buffer_id, - const gfx::Rect& damage_region) { + const gfx::Rect& damage_region, + bool wait_for_frame_callback) { DCHECK(base::CurrentUIThread::IsSet()); Surface* surface = GetSurface(wayland_surface); if (!surface || !ValidateBufferIdFromGpu(buffer_id)) return false; - if (!surface->CommitBuffer(buffer_id, damage_region)) { + if (!surface->CommitBuffer(buffer_id, damage_region, + wait_for_frame_callback)) { error_message_ = base::StrCat({"Buffer with ", NumberToString(buffer_id), " id does not exist or failed to be created."}); @@ -778,6 +824,24 @@ bool WaylandBufferManagerHost::CommitBufferInternal( return true; } +bool WaylandBufferManagerHost::CommitWithoutBufferInternal( + WaylandSurface* wayland_surface, + bool wait_for_frame_callback) { + DCHECK(base::CurrentUIThread::IsSet()); + + Surface* surface = GetSurface(wayland_surface); + if (!surface) + return false; + + bool result = surface->CommitBuffer(kInvalidBufferId, gfx::Rect(), + wait_for_frame_callback); + DCHECK(result); + + if (!error_message_.empty()) + TerminateGpuProcess(); + return true; +} + void WaylandBufferManagerHost::CommitBuffer(gfx::AcceleratedWidget widget, uint32_t buffer_id, const gfx::Rect& damage_region) { diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h b/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h index 20a974e1a5c..28dc77dc8d0 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h @@ -89,6 +89,9 @@ class WaylandBufferManagerHost : public ozone::mojom::WaylandBufferManagerHost, void OnSubsurfaceRemoved(WaylandWindow* window, WaylandSubsurface* subsurface) override; + // Start allowing attaching buffers to |surface|, same as + // OnWindowConfigured(), but for WaylandSurface. + void SetSurfaceConfigured(WaylandSurface* surface); void SetTerminateGpuCallback( base::OnceCallback<void(std::string)> terminate_gpu_cb); @@ -154,9 +157,22 @@ class WaylandBufferManagerHost : public ozone::mojom::WaylandBufferManagerHost, // |buffer_id| to a WaylandSurface. // Calls OnSubmission and OnPresentation on successful swap and pixels // presented. + // |wait_for_frame_callback| instructs that a surface should wait for previous + // wl_frame_callback. This is primarily used for sync wl_subsurfaces case + // where buffer updates within a frame should be seen together. A root_surface + // commit will move an entire wl_surface tree from pending state to ready + // state. This root_surface commit must wait for wl_frame_callback, such that + // in effect all other surface updates wait for this wl_frame_callback, too. bool CommitBufferInternal(WaylandSurface* wayland_surface, uint32_t buffer_id, - const gfx::Rect& damage_region); + const gfx::Rect& damage_region, + bool wait_for_frame_callback = true); + + // Does a wl_surface commit without attaching any buffers. This commit will + // still wait for previous wl_frame_callback. Similar to above but for + // commits that do not change the root_surface. + bool CommitWithoutBufferInternal(WaylandSurface* wayland_surface, + bool wait_for_frame_callback = true); // When a surface is hidden, the client may want to detach the buffer attached // to the surface to ensure Wayland does not present those contents and do not @@ -236,8 +252,13 @@ class WaylandBufferManagerHost : public ozone::mojom::WaylandBufferManagerHost, base::OnceCallback<void(std::string)> terminate_gpu_cb_; // Contains anonymous buffers aka buffers that are not attached to any of the - // existing surfaces and that will be mapped to surfaces later. Typically - // created when CreateAnonymousImage is called on the gpu process side. + // existing surfaces and that will be mapped to surfaces later. + // Typically created when CreateAnonymousImage is called on the gpu process + // side. + // We assume that a buffer_id/wl_buffer will never be used on multiple + // wl_surfaces so we never re-map buffers to surfaces. If we ever need to use + // the same buffer for 2 surfaces at the same time, create multiple wl_buffers + // referencing the same dmabuf or underlying storage. base::flat_map<uint32_t, std::unique_ptr<WaylandBuffer>> anonymous_buffers_; base::WeakPtrFactory<WaylandBufferManagerHost> weak_factory_; diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.cc b/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.cc index 7e1cdf1aee4..e39d1ee03fb 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.cc @@ -74,8 +74,7 @@ class ClipboardImpl final : public Clipboard, public DataSource::Delegate { source_.reset(); } else { data_ = *data; - if (!source_) - source_ = manager_->CreateSource(this); + source_ = manager_->CreateSource(this); source_->Offer(GetMimeTypes()); GetDevice()->SetSelectionSource(source_.get()); } diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_connection.cc b/chromium/ui/ozone/platform/wayland/host/wayland_connection.cc index febebefad2c..4c99dc8612c 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_connection.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_connection.cc @@ -38,6 +38,13 @@ #include "ui/ozone/platform/wayland/host/wayland_window.h" #include "ui/ozone/platform/wayland/host/wayland_window_drag_controller.h" #include "ui/ozone/platform/wayland/host/wayland_zwp_linux_dmabuf.h" +#include "ui/ozone/platform/wayland/host/xdg_foreign_wrapper.h" + +#if defined(USE_LIBWAYLAND_STUBS) +#include <dlfcn.h> + +#include "third_party/wayland/libwayland_stubs.h" // nogncheck +#endif namespace ui { @@ -46,15 +53,18 @@ constexpr uint32_t kMaxCompositorVersion = 4; constexpr uint32_t kMaxGtkPrimarySelectionDeviceManagerVersion = 1; constexpr uint32_t kMaxKeyboardExtensionVersion = 1; constexpr uint32_t kMaxLinuxDmabufVersion = 3; -constexpr uint32_t kMaxSeatVersion = 4; +constexpr uint32_t kMaxSeatVersion = 5; constexpr uint32_t kMaxShmVersion = 1; constexpr uint32_t kMaxXdgShellVersion = 1; constexpr uint32_t kMaxDeviceManagerVersion = 3; constexpr uint32_t kMaxWpPresentationVersion = 1; +constexpr uint32_t kMaxWpViewporterVersion = 1; constexpr uint32_t kMaxTextInputManagerVersion = 1; +constexpr uint32_t kMaxExplicitSyncVersion = 2; constexpr uint32_t kMinAuraShellVersion = 10; constexpr uint32_t kMinWlDrmVersion = 2; constexpr uint32_t kMinWlOutputVersion = 2; +constexpr uint32_t kMaxXdgDecorationVersion = 1; } // namespace WaylandConnection::WaylandConnection() = default; @@ -62,6 +72,21 @@ WaylandConnection::WaylandConnection() = default; WaylandConnection::~WaylandConnection() = default; bool WaylandConnection::Initialize() { +#if defined(USE_LIBWAYLAND_STUBS) + // Use RTLD_NOW to load all symbols, since the stubs will try to load all of + // them anyway. Use RTLD_GLOBAL to add the symbols to the global namespace. + auto dlopen_flags = RTLD_NOW | RTLD_GLOBAL; + if (void* libwayland_client = + dlopen("libwayland-client.so.0", dlopen_flags)) { + third_party_wayland::InitializeLibwaylandclient(libwayland_client); + } else { + LOG(ERROR) << "Failed to load wayland client libraries."; + return false; + } + if (void* libwayland_egl = dlopen("libwayland-egl.so.1", dlopen_flags)) + third_party_wayland::InitializeLibwaylandegl(libwayland_egl); +#endif + static const wl_registry_listener registry_listener = { &WaylandConnection::Global, &WaylandConnection::GlobalRemove, @@ -128,11 +153,15 @@ void WaylandConnection::ScheduleFlush() { } } +void WaylandConnection::SetShutdownCb(base::OnceCallback<void()> shutdown_cb) { + event_source()->SetShutdownCb(std::move(shutdown_cb)); +} + void WaylandConnection::SetCursorBitmap(const std::vector<SkBitmap>& bitmaps, const gfx::Point& location) { if (!cursor_) return; - cursor_->UpdateBitmap(bitmaps, location, serial_); + cursor_->UpdateBitmap(bitmaps, location, serial()); } bool WaylandConnection::IsDragInProgress() const { @@ -279,8 +308,7 @@ void WaylandConnection::Global(void* data, } zxdg_shell_v6_add_listener(connection->shell_v6_.get(), &shell_v6_listener, connection); - } else if (!connection->shell_v6_ && !connection->shell_ && - strcmp(interface, "xdg_wm_base") == 0) { + } else if (!connection->shell_ && strcmp(interface, "xdg_wm_base") == 0) { connection->shell_ = wl::Bind<xdg_wm_base>( registry, name, std::min(version, kMaxXdgShellVersion)); if (!connection->shell_) { @@ -331,6 +359,12 @@ void WaylandConnection::Global(void* data, connection->primary_selection_device_manager_ = std::make_unique<GtkPrimarySelectionDeviceManager>(manager.release(), connection); + } else if (!connection->linux_explicit_synchronization_ && + (strcmp(interface, "zwp_linux_explicit_synchronization_v1") == + 0)) { + connection->linux_explicit_synchronization_ = + wl::Bind<zwp_linux_explicit_synchronization_v1>( + registry, name, std::min(version, kMaxExplicitSyncVersion)); } else if (!connection->zwp_dmabuf_ && (strcmp(interface, "zwp_linux_dmabuf_v1") == 0)) { wl::Object<zwp_linux_dmabuf_v1> zwp_linux_dmabuf = @@ -342,6 +376,10 @@ void WaylandConnection::Global(void* data, (strcmp(interface, "wp_presentation") == 0)) { connection->presentation_ = wl::Bind<wp_presentation>(registry, name, kMaxWpPresentationVersion); + } else if (!connection->viewporter_ && + (strcmp(interface, "wp_viewporter") == 0)) { + connection->viewporter_ = + wl::Bind<wp_viewporter>(registry, name, kMaxWpViewporterVersion); } else if (!connection->keyboard_extension_v1_ && strcmp(interface, "zcr_keyboard_extension_v1") == 0) { connection->keyboard_extension_v1_ = wl::Bind<zcr_keyboard_extension_v1>( @@ -361,6 +399,10 @@ void WaylandConnection::Global(void* data, LOG(ERROR) << "Failed to bind to zwp_text_input_manager_v1 global"; return; } + } else if (!connection->xdg_foreign_ && + strcmp(interface, "zxdg_exporter_v1") == 0) { + connection->xdg_foreign_ = std::make_unique<XdgForeignWrapper>( + connection, wl::Bind<zxdg_exporter_v1>(registry, name, version)); } else if (!connection->drm_ && (strcmp(interface, "wl_drm") == 0) && version >= kMinWlDrmVersion) { auto wayland_drm = wl::Bind<struct wl_drm>(registry, name, version); @@ -375,6 +417,11 @@ void WaylandConnection::Global(void* data, LOG(ERROR) << "Failed to bind zaura_shell"; return; } + } else if (!connection->xdg_decoration_manager_ && + strcmp(interface, "zxdg_decoration_manager_v1") == 0) { + connection->xdg_decoration_manager_ = + wl::Bind<struct zxdg_decoration_manager_v1>(registry, name, + kMaxXdgDecorationVersion); } connection->ScheduleFlush(); diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_connection.h b/chromium/ui/ozone/platform/wayland/host/wayland_connection.h index 6dfc5b7a560..ec7beb98b17 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_connection.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_connection.h @@ -9,6 +9,7 @@ #include <vector> #include "third_party/skia/include/core/SkBitmap.h" +#include "ui/events/event.h" #include "ui/ozone/platform/wayland/common/wayland_object.h" #include "ui/ozone/platform/wayland/host/wayland_clipboard.h" #include "ui/ozone/platform/wayland/host/wayland_data_drag_controller.h" @@ -35,9 +36,16 @@ class WaylandDataDeviceManager; class WaylandCursorPosition; class WaylandWindowDragController; class GtkPrimarySelectionDeviceManager; +class XdgForeignWrapper; class WaylandConnection { public: + // Stores the last serial and the event type it is associated with. + struct EventSerial { + uint32_t serial = 0; + EventType event_type = EventType::ET_UNKNOWN; + }; + WaylandConnection(); WaylandConnection(const WaylandConnection&) = delete; WaylandConnection& operator=(const WaylandConnection&) = delete; @@ -48,10 +56,15 @@ class WaylandConnection { // Schedules a flush of the Wayland connection. void ScheduleFlush(); + // Sets a callback that that shutdowns the browser in case of unrecoverable + // error. Called by WaylandEventWatcher. + void SetShutdownCb(base::OnceCallback<void()> shutdown_cb); + wl_display* display() const { return display_.get(); } wl_compositor* compositor() const { return compositor_.get(); } uint32_t compositor_version() const { return compositor_version_; } wl_subcompositor* subcompositor() const { return subcompositor_.get(); } + wp_viewporter* viewporter() const { return viewporter_.get(); } xdg_wm_base* shell() const { return shell_.get(); } zxdg_shell_v6* shell_v6() const { return shell_v6_.get(); } zaura_shell* aura_shell() const { return aura_shell_.get(); } @@ -60,9 +73,19 @@ class WaylandConnection { zwp_text_input_manager_v1* text_input_manager_v1() const { return text_input_manager_v1_.get(); } + zwp_linux_explicit_synchronization_v1* linux_explicit_synchronization_v1() + const { + return linux_explicit_synchronization_.get(); + } + zxdg_decoration_manager_v1* xdg_decoration_manager_v1() const { + return xdg_decoration_manager_.get(); + } - void set_serial(uint32_t serial) { serial_ = serial; } - uint32_t serial() const { return serial_; } + void set_serial(uint32_t serial, EventType event_type) { + serial_ = {serial, event_type}; + } + uint32_t serial() const { return serial_.serial; } + EventSerial event_serial() const { return serial_; } void SetCursorBitmap(const std::vector<SkBitmap>& bitmaps, const gfx::Point& location); @@ -119,6 +142,8 @@ class WaylandConnection { return window_drag_controller_.get(); } + XdgForeignWrapper* xdg_foreign() const { return xdg_foreign_.get(); } + // Returns true when dragging is entered or started. bool IsDragInProgress() const; @@ -164,9 +189,13 @@ class WaylandConnection { wl::Object<xdg_wm_base> shell_; wl::Object<zxdg_shell_v6> shell_v6_; wl::Object<wp_presentation> presentation_; + wl::Object<wp_viewporter> viewporter_; wl::Object<zcr_keyboard_extension_v1> keyboard_extension_v1_; wl::Object<zwp_text_input_manager_v1> text_input_manager_v1_; wl::Object<zaura_shell> aura_shell_; + wl::Object<zwp_linux_explicit_synchronization_v1> + linux_explicit_synchronization_; + wl::Object<zxdg_decoration_manager_v1> xdg_decoration_manager_; // Event source instance. Must be declared before input objects so it // outlives them so thus being able to properly handle their destruction. @@ -186,6 +215,7 @@ class WaylandConnection { std::unique_ptr<WaylandDrm> drm_; std::unique_ptr<WaylandShm> shm_; std::unique_ptr<WaylandBufferManagerHost> buffer_manager_host_; + std::unique_ptr<XdgForeignWrapper> xdg_foreign_; std::unique_ptr<GtkPrimarySelectionDeviceManager> primary_selection_device_manager_; @@ -198,7 +228,7 @@ class WaylandConnection { bool scheduled_flush_ = false; - uint32_t serial_ = 0; + EventSerial serial_; }; } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_cursor.h b/chromium/ui/ozone/platform/wayland/host/wayland_cursor.h index 1f73050be9b..bb4978d2d3d 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_cursor.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_cursor.h @@ -5,8 +5,6 @@ #ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_CURSOR_H_ #define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_CURSOR_H_ -#include <wayland-client.h> - #include <vector> #include "base/containers/flat_map.h" diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_device.cc b/chromium/ui/ozone/platform/wayland/host/wayland_data_device.cc index f80738e5e34..8f4b62fa68e 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_data_device.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_device.cc @@ -109,14 +109,20 @@ void WaylandDataDevice::OnEnter(void* data, wl_fixed_t x, wl_fixed_t y, wl_data_offer* offer) { + auto* self = static_cast<WaylandDataDevice*>(data); WaylandWindow* window = wl::RootWindowFromWlSurface(surface); + + // During Chrome's tab dragging, when a browser window is quickly snapped in + // and out, it might get destroyed before the wl_data_device::enter event is + // processed for a drag offer. If this happens, |window| will be null here, so + // destroy |new_offer_| here, as some compositors assume it. Such behavior has + // been observed in Exosphere compositor, for example. if (!window) { - LOG(ERROR) << "Failed to get window."; + self->new_offer_.reset(); + VLOG(1) << "Failed to get window."; return; } - auto* self = static_cast<WaylandDataDevice*>(data); - // Null |drag_delegate_| here means that the DND session has been initiated by // an external application. In this case, use the default data drag delegate. if (!self->drag_delegate_) diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_device.h b/chromium/ui/ozone/platform/wayland/host/wayland_data_device.h index 12b84cf9cb1..f43ba2e8dfa 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_data_device.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_device.h @@ -5,8 +5,6 @@ #ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_DATA_DEVICE_H_ #define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_DATA_DEVICE_H_ -#include <wayland-client.h> - #include <cstdint> #include <memory> #include <string> diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_device_manager.cc b/chromium/ui/ozone/platform/wayland/host/wayland_data_device_manager.cc index 6c283794473..f800dcbd4fc 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_data_device_manager.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_device_manager.cc @@ -4,8 +4,6 @@ #include "ui/ozone/platform/wayland/host/wayland_data_device_manager.h" -#include <wayland-client-protocol.h> - #include <memory> #include "ui/ozone/platform/wayland/host/wayland_connection.h" diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.cc b/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.cc index e4337897d54..736c40d3cbe 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.cc @@ -4,8 +4,6 @@ #include "ui/ozone/platform/wayland/host/wayland_data_drag_controller.h" -#include <wayland-client-protocol.h> - #include <cstdint> #include "base/check.h" diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_offer.cc b/chromium/ui/ozone/platform/wayland/host/wayland_data_offer.cc index 1b274192123..84d29d32b60 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_data_offer.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_offer.cc @@ -26,7 +26,7 @@ WaylandDataOffer::~WaylandDataOffer() { void WaylandDataOffer::SetAction(uint32_t dnd_actions, uint32_t preferred_action) { - if (wl_data_offer_get_version(data_offer_.get()) >= + if (wl::get_version_of_object(data_offer_.get()) >= WL_DATA_OFFER_SET_ACTIONS_SINCE_VERSION) { wl_data_offer_set_actions(data_offer_.get(), dnd_actions, preferred_action); } @@ -62,9 +62,10 @@ base::ScopedFD WaylandDataOffer::Receive(const std::string& mime_type) { } void WaylandDataOffer::FinishOffer() { - if (wl_data_offer_get_version(data_offer_.get()) >= - WL_DATA_OFFER_FINISH_SINCE_VERSION) + if (wl::get_version_of_object(data_offer_.get()) >= + WL_DATA_OFFER_FINISH_SINCE_VERSION) { wl_data_offer_finish(data_offer_.get()); + } } uint32_t WaylandDataOffer::source_actions() const { diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_offer.h b/chromium/ui/ozone/platform/wayland/host/wayland_data_offer.h index f79bc7b3b71..a724709c9c1 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_data_offer.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_offer.h @@ -5,8 +5,6 @@ #ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_DATA_OFFER_H_ #define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_DATA_OFFER_H_ -#include <wayland-client.h> - #include <string> #include "base/files/scoped_file.h" diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_source.cc b/chromium/ui/ozone/platform/wayland/host/wayland_data_source.cc index 1d66a9f74cb..aaaf384f3e9 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_data_source.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_source.cc @@ -5,7 +5,6 @@ #include "ui/ozone/platform/wayland/host/wayland_data_source.h" #include <gtk-primary-selection-client-protocol.h> -#include <wayland-client-protocol.h> #include <cstdint> #include <vector> @@ -111,7 +110,7 @@ void DataSource<T>::SetAction(int operation) { template <> void DataSource<wl_data_source>::SetAction(int operation) { - if (wl_data_source_get_version(data_source_.get()) >= + if (wl::get_version_of_object(data_source_.get()) >= WL_DATA_SOURCE_SET_ACTIONS_SINCE_VERSION) { uint32_t dnd_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; if (operation & ui::DragDropTypes::DRAG_COPY) diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_event_source.cc b/chromium/ui/ozone/platform/wayland/host/wayland_event_source.cc index 41f186362f9..15eb6b6e2c2 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_event_source.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_event_source.cc @@ -37,6 +37,12 @@ bool HasAnyPointerButtonFlag(int flags) { EF_FORWARD_MOUSE_BUTTON)) != 0; } +// Number of fingers for scroll gestures. +constexpr int kGestureScrollFingerCount = 2; + +// Maximum size of the stored recent pointer frame information. +constexpr int kRecentPointerFrameMaxSize = 20; + } // namespace struct WaylandEventSource::TouchPoint { @@ -67,6 +73,10 @@ WaylandEventSource::WaylandEventSource(wl_display* display, WaylandEventSource::~WaylandEventSource() = default; +void WaylandEventSource::SetShutdownCb(base::OnceCallback<void()> shutdown_cb) { + event_watcher_->SetShutdownCb(std::move(shutdown_cb)); +} + bool WaylandEventSource::StartProcessingEvents() { return event_watcher_->StartProcessingEvents(); } @@ -98,17 +108,15 @@ void WaylandEventSource::OnKeyboardModifiersChanged(int modifiers) { uint32_t WaylandEventSource::OnKeyboardKeyEvent(EventType type, DomCode dom_code, - DomKey dom_key, - KeyboardCode key_code, bool repeat, base::TimeTicks timestamp) { DCHECK(type == ET_KEY_PRESSED || type == ET_KEY_RELEASED); if (!keyboard_) return POST_DISPATCH_NONE; - // try to decode key, if not yet. - if (dom_key == DomKey::NONE && - !keyboard_->Decode(dom_code, keyboard_modifiers_, &dom_key, &key_code)) { + DomKey dom_key; + KeyboardCode key_code; + if (!keyboard_->Decode(dom_code, keyboard_modifiers_, &dom_key, &key_code)) { LOG(ERROR) << "Failed to decode key event."; return POST_DISPATCH_NONE; } @@ -200,6 +208,51 @@ void WaylandEventSource::OnPointerAxisEvent(const gfx::Vector2d& offset) { MouseWheelEvent event(offset, pointer_location_, pointer_location_, EventTimeForNow(), flags, 0); DispatchEvent(&event); + current_pointer_frame_.dx += offset.x(); + current_pointer_frame_.dy += offset.y(); +} + +void WaylandEventSource::OnPointerFrameEvent() { + base::TimeTicks now = EventTimeForNow(); + current_pointer_frame_.dt = now - last_pointer_frame_time_; + last_pointer_frame_time_ = now; + + // Dispatch Fling event if pointer.axis_stop is notified and the recent + // pointer.axis events meets the criteria to start fling scroll. + if (current_pointer_frame_.dx == 0 && current_pointer_frame_.dy == 0 && + current_pointer_frame_.is_axis_stop) { + gfx::Vector2dF initial_velocity = ComputeFlingVelocity(); + float vx = initial_velocity.x(); + float vy = initial_velocity.y(); + ScrollEvent event( + vx == 0 && vy == 0 ? ET_SCROLL_FLING_CANCEL : ET_SCROLL_FLING_START, + pointer_location_, pointer_location_, now, + pointer_flags_ | keyboard_modifiers_, vx, vy, vx, vy, + kGestureScrollFingerCount); + DispatchEvent(&event); + recent_pointer_frames_.clear(); + } else { + if (recent_pointer_frames_.size() + 1 > kRecentPointerFrameMaxSize) + recent_pointer_frames_.pop_back(); + recent_pointer_frames_.push_front(current_pointer_frame_); + } + // Reset |current_pointer_frame_|. + current_pointer_frame_.dx = 0; + current_pointer_frame_.dy = 0; + current_pointer_frame_.is_axis_stop = false; +} + +void WaylandEventSource::OnPointerAxisSourceEvent(uint32_t axis_source) { + current_pointer_frame_.axis_source = axis_source; +} + +void WaylandEventSource::OnPointerAxisStopEvent(uint32_t axis) { + if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) { + current_pointer_frame_.dy = 0; + } else if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) { + current_pointer_frame_.dx = 0; + } + current_pointer_frame_.is_axis_stop = true; } void WaylandEventSource::OnTouchCreated(WaylandTouch* touch) { @@ -359,4 +412,28 @@ bool WaylandEventSource::ShouldUnsetTouchFocus(WaylandWindow* win, return result == touch_points_.end(); } +gfx::Vector2dF WaylandEventSource::ComputeFlingVelocity() { + // Return average velocity in the last 200ms. + // TODO(fukino): Make the formula similar to libgestures's + // RegressScrollVelocity(). crbug.com/1129263. + base::TimeDelta dt; + float dx = 0.0f; + float dy = 0.0f; + for (auto& frame : recent_pointer_frames_) { + if (frame.axis_source != WL_POINTER_AXIS_SOURCE_FINGER) + break; + if (frame.dx == 0 && frame.dy == 0) + break; + if (dt + frame.dt > base::TimeDelta::FromMilliseconds(200)) + break; + + dx += frame.dx; + dy += frame.dy; + dt += frame.dt; + } + float dt_inv = 1.0f / dt.InSecondsF(); + return dt.is_zero() ? gfx::Vector2dF() + : gfx::Vector2dF(dx * dt_inv, dy * dt_inv); +} + } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_event_source.h b/chromium/ui/ozone/platform/wayland/host/wayland_event_source.h index e646df4fd4d..dfda62810e5 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_event_source.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_event_source.h @@ -5,6 +5,7 @@ #ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_EVENT_SOURCE_H_ #define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_EVENT_SOURCE_H_ +#include <deque> #include <memory> #include "base/containers/flat_map.h" @@ -12,8 +13,6 @@ #include "base/time/time.h" #include "ui/events/event_constants.h" #include "ui/events/keycodes/dom/dom_code.h" -#include "ui/events/keycodes/dom/dom_key.h" -#include "ui/events/keycodes/keyboard_codes.h" #include "ui/events/platform/platform_event_source.h" #include "ui/events/pointer_details.h" #include "ui/events/types/event_type.h" @@ -61,6 +60,10 @@ class WaylandEventSource : public PlatformEventSource, int keyboard_modifiers() const { return keyboard_modifiers_; } + // Sets a callback that that shutdowns the browser in case of unrecoverable + // error. Called by WaylandEventWatcher. + void SetShutdownCb(base::OnceCallback<void()> shutdown_cb); + // Starts polling for events from the wayland connection file descriptor. // This method assumes connection is already estabilished and input objects // are already bound and properly initialized. @@ -84,8 +87,6 @@ class WaylandEventSource : public PlatformEventSource, void OnKeyboardModifiersChanged(int modifiers) override; uint32_t OnKeyboardKeyEvent(EventType type, DomCode dom_code, - DomKey dom_key, - KeyboardCode key_code, bool repeat, base::TimeTicks timestamp) override; @@ -99,6 +100,9 @@ class WaylandEventSource : public PlatformEventSource, WaylandWindow* window = nullptr) override; void OnPointerMotionEvent(const gfx::PointF& location) override; void OnPointerAxisEvent(const gfx::Vector2d& offset) override; + void OnPointerFrameEvent() override; + void OnPointerAxisSourceEvent(uint32_t axis_source) override; + void OnPointerAxisStopEvent(uint32_t axis) override; // WaylandTouch::Delegate void OnTouchCreated(WaylandTouch* touch) override; @@ -114,6 +118,13 @@ class WaylandEventSource : public PlatformEventSource, void OnTouchCancelEvent() override; private: + struct PointerFrame { + uint32_t axis_source = WL_POINTER_AXIS_SOURCE_WHEEL; + float dx = 0.0f; + float dy = 0.0f; + base::TimeDelta dt; + bool is_axis_stop = false; + }; struct TouchPoint; // PlatformEventSource: @@ -130,6 +141,9 @@ class WaylandEventSource : public PlatformEventSource, base::Optional<PointerId> id = base::nullopt); bool ShouldUnsetTouchFocus(WaylandWindow* window, PointerId id); + // Computes initial velocity of fling scroll based on recent frames. + gfx::Vector2dF ComputeFlingVelocity(); + WaylandWindowManager* const window_manager_; // Input device objects. Owned by WaylandConnection. @@ -149,6 +163,16 @@ class WaylandEventSource : public PlatformEventSource, // Last known pointer location. gfx::PointF pointer_location_; + // Current frame + PointerFrame current_pointer_frame_; + + // Time of the last pointer frame event. + base::TimeTicks last_pointer_frame_time_; + + // Recent pointer frames to compute fling scroll. + // Front is newer, and back is older. + std::deque<PointerFrame> recent_pointer_frames_; + // The window the pointer is over. WaylandWindow* window_with_pointer_focus_ = nullptr; diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_event_source_unittest.cc b/chromium/ui/ozone/platform/wayland/host/wayland_event_source_unittest.cc index 34bf114fa03..fe57fe1421e 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_event_source_unittest.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_event_source_unittest.cc @@ -3,8 +3,6 @@ // found in the LICENSE file. #include <linux/input.h> -#include <wayland-client-protocol.h> -#include <wayland-server-core.h> #include "testing/gtest/include/gtest/gtest.h" #include "ui/ozone/platform/wayland/host/wayland_event_source.h" diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_event_watcher.cc b/chromium/ui/ozone/platform/wayland/host/wayland_event_watcher.cc index 52f5eb0e870..9fd65bd9afa 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_event_watcher.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_event_watcher.cc @@ -4,13 +4,11 @@ #include "ui/ozone/platform/wayland/host/wayland_event_watcher.h" -#include <wayland-client-core.h> -#include <wayland-client-protocol.h> - #include "base/bind.h" #include "base/check.h" #include "base/task/current_thread.h" #include "ui/events/event.h" +#include "ui/ozone/platform/wayland/common/wayland.h" namespace ui { @@ -23,6 +21,12 @@ WaylandEventWatcher::~WaylandEventWatcher() { StopProcessingEvents(); } +void WaylandEventWatcher::SetShutdownCb( + base::OnceCallback<void()> shutdown_cb) { + DCHECK(shutdown_cb_.is_null()); + shutdown_cb_ = std::move(shutdown_cb); +} + bool WaylandEventWatcher::StartProcessingEvents() { DCHECK(display_); if (watching_) @@ -44,6 +48,11 @@ bool WaylandEventWatcher::StopProcessingEvents() { } void WaylandEventWatcher::OnFileCanReadWithoutBlocking(int fd) { + if (!CheckForErrors()) { + StopProcessingEvents(); + return; + } + if (prepared_) { prepared_ = false; if (wl_display_read_events(display_) == -1) @@ -104,4 +113,15 @@ void WaylandEventWatcher::MaybePrepareReadQueue() { wl_display_dispatch_pending(display_); } +bool WaylandEventWatcher::CheckForErrors() { + int err = wl_display_get_error(display_); + if (err == EPROTO) { + // This can be null in tests. + if (!shutdown_cb_.is_null()) + std::move(shutdown_cb_).Run(); + return false; + } + return true; +} + } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_event_watcher.h b/chromium/ui/ozone/platform/wayland/host/wayland_event_watcher.h index 9960855cd5a..6a13ea92f4b 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_event_watcher.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_event_watcher.h @@ -5,6 +5,7 @@ #ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_EVENT_WATCHER_H_ #define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_EVENT_WATCHER_H_ +#include "base/callback.h" #include "base/message_loop/message_pump_for_ui.h" #include "base/message_loop/watchable_io_message_pump_posix.h" @@ -23,6 +24,10 @@ class WaylandEventWatcher : public base::MessagePumpForUI::FdWatcher { WaylandEventWatcher& operator=(const WaylandEventWatcher&) = delete; ~WaylandEventWatcher() override; + // Sets a callback that that shutdowns the browser in case of unrecoverable + // error. Can only be set once. + void SetShutdownCb(base::OnceCallback<void()> shutdown_cb); + // Starts polling for events from the wayland connection file descriptor. // This method assumes connection is already estabilished and input objects // are already bound and properly initialized. @@ -39,12 +44,18 @@ class WaylandEventWatcher : public base::MessagePumpForUI::FdWatcher { bool StartWatchingFd(base::WatchableIOMessagePumpPosix::Mode mode); void MaybePrepareReadQueue(); + // Checks if |display_| has any error set. If so, |shutdown_cb_| is executed + // and false is returned. + bool CheckForErrors(); + base::MessagePumpForUI::FdWatchController controller_; wl_display* const display_; // Owned by WaylandConnection. bool watching_ = false; bool prepared_ = false; + + base::OnceCallback<void()> shutdown_cb_; }; } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_input_method_context.cc b/chromium/ui/ozone/platform/wayland/host/wayland_input_method_context.cc index d03e64f34a7..8669a18a8b5 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_input_method_context.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_input_method_context.cc @@ -15,10 +15,7 @@ #include "ui/events/event.h" #include "ui/events/keycodes/dom/dom_code.h" #include "ui/events/keycodes/dom/keycode_converter.h" -#include "ui/events/keycodes/keyboard_code_conversion.h" -#include "ui/events/keycodes/keyboard_code_conversion_xkb.h" -#include "ui/events/ozone/layout/keyboard_layout_engine.h" -#include "ui/events/ozone/layout/keyboard_layout_engine_manager.h" +#include "ui/events/ozone/evdev/keyboard_util_evdev.h" #include "ui/events/types/event_type.h" #include "ui/gfx/range/range.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" @@ -27,12 +24,6 @@ namespace ui { -namespace { - -constexpr int kXkbKeycodeOffset = 8; - -} // namespace - WaylandInputMethodContext::WaylandInputMethodContext( WaylandConnection* connection, WaylandKeyboard::Delegate* key_delegate, @@ -160,18 +151,16 @@ void WaylandInputMethodContext::OnDeleteSurroundingText(int32_t index, void WaylandInputMethodContext::OnKeysym(uint32_t key, uint32_t state, uint32_t modifiers) { - DomKey dom_key = NonPrintableXKeySymToDomKey(key); - KeyboardCode key_code = NonPrintableDomKeyToKeyboardCode(dom_key); DomCode dom_code = - KeycodeConverter::NativeKeycodeToDomCode(key_code + kXkbKeycodeOffset); + KeycodeConverter::NativeKeycodeToDomCode(EvdevCodeToNativeCode(key)); if (dom_code == ui::DomCode::NONE) return; // TODO(crbug.com/1079353): Handle modifiers. EventType type = state == WL_KEYBOARD_KEY_STATE_PRESSED ? ET_KEY_PRESSED : ET_KEY_RELEASED; - key_delegate_->OnKeyboardKeyEvent(type, dom_code, dom_key, key_code, - /*repeat=*/false, EventTimeForNow()); + key_delegate_->OnKeyboardKeyEvent(type, dom_code, /*repeat=*/false, + EventTimeForNow()); } } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.cc b/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.cc index a4c905b7895..dee622c3f50 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.cc @@ -130,9 +130,9 @@ void WaylandKeyboard::Key(void* data, WaylandKeyboard* keyboard = static_cast<WaylandKeyboard*>(data); DCHECK(keyboard); - keyboard->connection_->set_serial(serial); - bool down = state == WL_KEYBOARD_KEY_STATE_PRESSED; + if (down) + keyboard->connection_->set_serial(serial, ET_KEY_PRESSED); int device_id = keyboard->device_id(); keyboard->auto_repeat_handler_.UpdateKeyRepeat( @@ -201,8 +201,7 @@ void WaylandKeyboard::DispatchKey(uint32_t key, // Pass empty DomKey and KeyboardCode here so the delegate can pre-process // and decode it when needed. uint32_t result = delegate_->OnKeyboardKeyEvent( - down ? ET_KEY_PRESSED : ET_KEY_RELEASED, dom_code, DomKey::NONE, - KeyboardCode::VKEY_UNKNOWN, repeat, timestamp); + down ? ET_KEY_PRESSED : ET_KEY_RELEASED, dom_code, repeat, timestamp); if (extended_keyboard_v1_) { bool handled = result & POST_DISPATCH_STOP_PROPAGATION; diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.h b/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.h index e449da0be85..abedf290672 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.h @@ -6,7 +6,6 @@ #define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_KEYBOARD_H_ #include <keyboard-extension-unstable-v1-client-protocol.h> -#include <wayland-client.h> #include "base/time/time.h" #include "ui/base/buildflags.h" @@ -117,8 +116,6 @@ class WaylandKeyboard::Delegate { // dispatched. virtual uint32_t OnKeyboardKeyEvent(EventType type, DomCode dom_code, - DomKey dom_key, - KeyboardCode key_code, bool repeat, base::TimeTicks timestamp) = 0; diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_output.cc b/chromium/ui/ozone/platform/wayland/host/wayland_output.cc index 7a4067df952..92d0f5be651 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_output.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_output.cc @@ -4,8 +4,6 @@ #include "ui/ozone/platform/wayland/host/wayland_output.h" -#include <wayland-client.h> - #include "ui/gfx/color_space.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_pointer.cc b/chromium/ui/ozone/platform/wayland/host/wayland_pointer.cc index 13cb8c0a3fc..b4fb4f09c5c 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_pointer.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_pointer.cc @@ -5,8 +5,6 @@ #include "ui/ozone/platform/wayland/host/wayland_pointer.h" #include <linux/input.h> -#include <wayland-client-protocol.h> -#include <wayland-client.h> #include "ui/events/event.h" #include "ui/events/types/event_type.h" @@ -23,9 +21,11 @@ WaylandPointer::WaylandPointer(wl_pointer* pointer, Delegate* delegate) : obj_(pointer), connection_(connection), delegate_(delegate) { static const wl_pointer_listener listener = { - &WaylandPointer::Enter, &WaylandPointer::Leave, &WaylandPointer::Motion, - &WaylandPointer::Button, &WaylandPointer::Axis, - }; + &WaylandPointer::Enter, &WaylandPointer::Leave, + &WaylandPointer::Motion, &WaylandPointer::Button, + &WaylandPointer::Axis, &WaylandPointer::Frame, + &WaylandPointer::AxisSource, &WaylandPointer::AxisStop, + &WaylandPointer::AxisDiscrete}; DCHECK(delegate_); delegate_->OnPointerCreated(this); @@ -105,14 +105,10 @@ void WaylandPointer::Button(void* data, return; } - // Set serial only on button presses. Popup windows can be created on - // button/touch presses, and, thus, require the serial of the last serial when - // the button was pressed. Otherwise, Wayland server dismisses the popup - // requests (see the protocol definition). - if (state == WL_POINTER_BUTTON_STATE_PRESSED) - pointer->connection_->set_serial(serial); EventType type = state == WL_POINTER_BUTTON_STATE_PRESSED ? ET_MOUSE_PRESSED : ET_MOUSE_RELEASED; + if (type == ET_MOUSE_PRESSED) + pointer->connection_->set_serial(serial, type); pointer->delegate_->OnPointerButtonEvent(type, changed_button); } @@ -142,4 +138,37 @@ void WaylandPointer::Axis(void* data, pointer->delegate_->OnPointerAxisEvent(offset); } +// static +void WaylandPointer::Frame(void* data, wl_pointer* obj) { + WaylandPointer* pointer = static_cast<WaylandPointer*>(data); + pointer->delegate_->OnPointerFrameEvent(); +} + +// static +void WaylandPointer::AxisSource(void* data, + wl_pointer* obj, + uint32_t axis_source) { + WaylandPointer* pointer = static_cast<WaylandPointer*>(data); + pointer->delegate_->OnPointerAxisSourceEvent(axis_source); +} + +// static +void WaylandPointer::AxisStop(void* data, + wl_pointer* obj, + uint32_t time, + uint32_t axis) { + WaylandPointer* pointer = static_cast<WaylandPointer*>(data); + pointer->delegate_->OnPointerAxisStopEvent(axis); +} + +// static +void WaylandPointer::AxisDiscrete(void* data, + wl_pointer* obj, + uint32_t axis, + int32_t discrete) { + // TODO(fukino): Use this events for better handling of mouse wheel events. + // crbug.com/1129259. + NOTIMPLEMENTED_LOG_ONCE(); +} + } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_pointer.h b/chromium/ui/ozone/platform/wayland/host/wayland_pointer.h index c5cf9f7dccc..2ebb8419769 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_pointer.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_pointer.h @@ -60,6 +60,16 @@ class WaylandPointer { uint32_t time, uint32_t axis, wl_fixed_t value); + static void Frame(void* data, wl_pointer* obj); + static void AxisSource(void* data, wl_pointer* obj, uint32_t axis_source); + static void AxisStop(void* data, + wl_pointer* obj, + uint32_t time, + uint32_t axis); + static void AxisDiscrete(void* data, + wl_pointer* obj, + uint32_t axis, + int32_t discrete); wl::Object<wl_pointer> obj_; WaylandConnection* const connection_; @@ -79,6 +89,9 @@ class WaylandPointer::Delegate { WaylandWindow* window = nullptr) = 0; virtual void OnPointerMotionEvent(const gfx::PointF& location) = 0; virtual void OnPointerAxisEvent(const gfx::Vector2d& offset) = 0; + virtual void OnPointerFrameEvent() = 0; + virtual void OnPointerAxisSourceEvent(uint32_t axis_source) = 0; + virtual void OnPointerAxisStopEvent(uint32_t axis) = 0; }; } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_pointer_unittest.cc b/chromium/ui/ozone/platform/wayland/host/wayland_pointer_unittest.cc index 41dfb730784..8e6fd03896b 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_pointer_unittest.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_pointer_unittest.cc @@ -4,6 +4,8 @@ #include <linux/input.h> #include <wayland-server.h> + +#include <cmath> #include <memory> #include "testing/gmock/include/gmock/gmock.h" @@ -49,6 +51,35 @@ class WaylandPointerTest : public WaylandTest { DISALLOW_COPY_AND_ASSIGN(WaylandPointerTest); }; +void SendAxisEvents(struct wl_resource* resource, + uint32_t time_ms, + uint32_t axis_source, + uint32_t axis, + int offset) { + wl_pointer_send_axis_source(resource, axis_source); + wl_pointer_send_axis(resource, time_ms, axis, wl_fixed_from_int(offset)); + wl_pointer_send_frame(resource); +} + +void SendDiagonalAxisEvents(struct wl_resource* resource, + uint32_t time_ms, + uint32_t axis_source, + int offset_x, + int offset_y) { + wl_pointer_send_axis_source(resource, axis_source); + wl_pointer_send_axis(resource, time_ms, WL_POINTER_AXIS_VERTICAL_SCROLL, + wl_fixed_from_int(offset_y)); + wl_pointer_send_axis(resource, time_ms, WL_POINTER_AXIS_HORIZONTAL_SCROLL, + wl_fixed_from_int(offset_x)); + wl_pointer_send_frame(resource); +} + +void SendAxisStopEvents(struct wl_resource* resource, uint32_t time) { + wl_pointer_send_axis_stop(resource, time, WL_POINTER_AXIS_VERTICAL_SCROLL); + wl_pointer_send_axis_stop(resource, time, WL_POINTER_AXIS_HORIZONTAL_SCROLL); + wl_pointer_send_frame(resource); +} + ACTION_P(CloneEvent, ptr) { *ptr = Event::Clone(*arg0); } @@ -262,6 +293,216 @@ TEST_P(WaylandPointerTest, SetBitmapOnPointerFocus) { Mock::VerifyAndClearExpectations(pointer_); } +TEST_P(WaylandPointerTest, FlingVertical) { + uint32_t serial = 0; + uint32_t time = 1001; + wl_pointer_send_enter(pointer_->resource(), ++serial, surface_->resource(), + wl_fixed_from_int(50), wl_fixed_from_int(75)); + wl_pointer_send_button(pointer_->resource(), ++serial, ++time, BTN_RIGHT, + WL_POINTER_BUTTON_STATE_PRESSED); + + Sync(); + + std::unique_ptr<Event> event1, event2, event3; + EXPECT_CALL(delegate_, DispatchEvent(_)) + .Times(3) + .WillOnce(CloneEvent(&event1)) + .WillOnce(CloneEvent(&event2)) + .WillOnce(CloneEvent(&event3)); + // 1st axis event. + SendAxisEvents(pointer_->resource(), ++time, WL_POINTER_AXIS_SOURCE_FINGER, + WL_POINTER_AXIS_VERTICAL_SCROLL, 10); + // 2nd axis event. + SendAxisEvents(pointer_->resource(), ++time, WL_POINTER_AXIS_SOURCE_FINGER, + WL_POINTER_AXIS_VERTICAL_SCROLL, 10); + // axis_stop event which should trigger fling scroll. + SendAxisStopEvents(pointer_->resource(), ++time); + + Sync(); + + // Usual axis events should follow before the fling event. + ASSERT_TRUE(event1); + ASSERT_TRUE(event1->IsMouseWheelEvent()); + ASSERT_TRUE(event2); + ASSERT_TRUE(event2->IsMouseWheelEvent()); + + // The third dispatched event should be FLING_START. + ASSERT_TRUE(event3); + ASSERT_TRUE(event3->IsScrollEvent()); + auto* scroll_event = event3->AsScrollEvent(); + EXPECT_EQ(ET_SCROLL_FLING_START, scroll_event->type()); + EXPECT_EQ(gfx::PointF(50, 75), scroll_event->location_f()); + EXPECT_EQ(0.0f, scroll_event->x_offset()); + // Initial vertical velocity depends on the implementation outside of + // WaylandPointer, but it should be negative value based on the direction of + // recent two axis events. + EXPECT_GT(0.0f, scroll_event->y_offset()); + EXPECT_EQ(0.0f, scroll_event->x_offset_ordinal()); + EXPECT_GT(0.0f, scroll_event->y_offset_ordinal()); +} + +TEST_P(WaylandPointerTest, FlingHorizontal) { + uint32_t serial = 0; + uint32_t time = 1001; + wl_pointer_send_enter(pointer_->resource(), ++serial, surface_->resource(), + wl_fixed_from_int(50), wl_fixed_from_int(75)); + wl_pointer_send_button(pointer_->resource(), ++serial, ++time, BTN_RIGHT, + WL_POINTER_BUTTON_STATE_PRESSED); + + Sync(); + + std::unique_ptr<Event> event1, event2, event3; + EXPECT_CALL(delegate_, DispatchEvent(_)) + .Times(3) + .WillOnce(CloneEvent(&event1)) + .WillOnce(CloneEvent(&event2)) + .WillOnce(CloneEvent(&event3)); + // 1st axis event. + SendAxisEvents(pointer_->resource(), ++time, WL_POINTER_AXIS_SOURCE_FINGER, + WL_POINTER_AXIS_HORIZONTAL_SCROLL, 10); + // 2nd axis event. + SendAxisEvents(pointer_->resource(), ++time, WL_POINTER_AXIS_SOURCE_FINGER, + WL_POINTER_AXIS_HORIZONTAL_SCROLL, 10); + // axis_stop event which should trigger fling scroll. + SendAxisStopEvents(pointer_->resource(), ++time); + + Sync(); + + // Usual axis events should follow before the fling event. + ASSERT_TRUE(event1); + ASSERT_TRUE(event1->IsMouseWheelEvent()); + ASSERT_TRUE(event2); + ASSERT_TRUE(event2->IsMouseWheelEvent()); + + // The third dispatched event should be FLING_START. + ASSERT_TRUE(event3); + ASSERT_TRUE(event3->IsScrollEvent()); + auto* scroll_event = event3->AsScrollEvent(); + EXPECT_EQ(ET_SCROLL_FLING_START, scroll_event->type()); + EXPECT_EQ(gfx::PointF(50, 75), scroll_event->location_f()); + // Initial horizontal velocity depends on the implementation outside of + // WaylandPointer, but it should be positive value based on the direction of + // recent two axis events. + EXPECT_LT(0.0f, scroll_event->x_offset()); + EXPECT_EQ(0.0f, scroll_event->y_offset()); + EXPECT_LT(0.0f, scroll_event->x_offset_ordinal()); + EXPECT_EQ(0.0f, scroll_event->y_offset_ordinal()); +} + +TEST_P(WaylandPointerTest, FlingCancel) { + uint32_t serial = 0; + uint32_t time = 1001; + wl_pointer_send_enter(pointer_->resource(), ++serial, surface_->resource(), + wl_fixed_from_int(50), wl_fixed_from_int(75)); + wl_pointer_send_button(pointer_->resource(), ++serial, ++time, BTN_RIGHT, + WL_POINTER_BUTTON_STATE_PRESSED); + + Sync(); + + std::unique_ptr<Event> event1, event2, event3, event4; + EXPECT_CALL(delegate_, DispatchEvent(_)) + .Times(4) + .WillOnce(CloneEvent(&event1)) + .WillOnce(CloneEvent(&event2)) + .WillOnce(CloneEvent(&event3)) + .WillOnce(CloneEvent(&event4)); + // 1st axis event. + SendAxisEvents(pointer_->resource(), ++time, WL_POINTER_AXIS_SOURCE_FINGER, + WL_POINTER_AXIS_VERTICAL_SCROLL, 10); + // 2nd axis event. + SendAxisEvents(pointer_->resource(), ++time, WL_POINTER_AXIS_SOURCE_FINGER, + WL_POINTER_AXIS_VERTICAL_SCROLL, 10); + // 3rd axis event, whose offset is 0, should make the following axis_stop + // trigger fling cancel. + SendAxisEvents(pointer_->resource(), ++time, WL_POINTER_AXIS_SOURCE_FINGER, + WL_POINTER_AXIS_VERTICAL_SCROLL, 0); + // axis_stop event which should trigger fling cancel. + SendAxisStopEvents(pointer_->resource(), ++time); + + Sync(); + + // Usual axis events should follow before the fling event. + ASSERT_TRUE(event1); + ASSERT_TRUE(event1->IsMouseWheelEvent()); + ASSERT_TRUE(event2); + ASSERT_TRUE(event2->IsMouseWheelEvent()); + + // The 3rd axis event's offset is 0. + ASSERT_TRUE(event3); + ASSERT_TRUE(event3->IsMouseWheelEvent()); + auto* mouse_wheel_event = event3->AsMouseWheelEvent(); + EXPECT_EQ(gfx::Vector2d(0, 0), mouse_wheel_event->offset()); + + // The 4th event should be FLING_CANCEL. + ASSERT_TRUE(event4); + ASSERT_TRUE(event4->IsScrollEvent()); + auto* scroll_event = event4->AsScrollEvent(); + EXPECT_EQ(ET_SCROLL_FLING_CANCEL, scroll_event->type()); + EXPECT_EQ(gfx::PointF(50, 75), scroll_event->location_f()); + EXPECT_EQ(0.0f, scroll_event->x_offset()); + EXPECT_EQ(0.0f, scroll_event->y_offset()); + EXPECT_EQ(0.0f, scroll_event->x_offset_ordinal()); + EXPECT_EQ(0.0f, scroll_event->y_offset_ordinal()); +} + +TEST_P(WaylandPointerTest, FlingDiagonal) { + uint32_t serial = 0; + uint32_t time = 1001; + wl_pointer_send_enter(pointer_->resource(), ++serial, surface_->resource(), + wl_fixed_from_int(50), wl_fixed_from_int(75)); + wl_pointer_send_button(pointer_->resource(), ++serial, ++time, BTN_RIGHT, + WL_POINTER_BUTTON_STATE_PRESSED); + + Sync(); + + std::unique_ptr<Event> event1, event2, event3, event4, event5; + EXPECT_CALL(delegate_, DispatchEvent(_)) + .Times(5) + .WillOnce(CloneEvent(&event1)) + .WillOnce(CloneEvent(&event2)) + .WillOnce(CloneEvent(&event3)) + .WillOnce(CloneEvent(&event4)) + .WillOnce(CloneEvent(&event5)); + // 1st axis event notifies scrolls both in vertical and horizontal. + SendDiagonalAxisEvents(pointer_->resource(), ++time, + WL_POINTER_AXIS_SOURCE_FINGER, 20, 10); + // 2st axis event notifies scrolls both in vertical and horizontal. + SendDiagonalAxisEvents(pointer_->resource(), ++time, + WL_POINTER_AXIS_SOURCE_FINGER, 20, 10); + // axis_stop event which should trigger fling scroll. + SendAxisStopEvents(pointer_->resource(), ++time); + + Sync(); + + // Usual axis events should follow before the fling event. + ASSERT_TRUE(event1); + ASSERT_TRUE(event1->IsMouseWheelEvent()); + ASSERT_TRUE(event2); + ASSERT_TRUE(event2->IsMouseWheelEvent()); + ASSERT_TRUE(event3); + ASSERT_TRUE(event3->IsMouseWheelEvent()); + ASSERT_TRUE(event4); + ASSERT_TRUE(event4->IsMouseWheelEvent()); + + // The third dispatched event should be FLING_START. + ASSERT_TRUE(event5); + ASSERT_TRUE(event5->IsScrollEvent()); + auto* scroll_event = event5->AsScrollEvent(); + EXPECT_EQ(ET_SCROLL_FLING_START, scroll_event->type()); + EXPECT_EQ(gfx::PointF(50, 75), scroll_event->location_f()); + // Check the offset direction. It should non-zero in both directions. + EXPECT_LT(0.0f, scroll_event->x_offset()); + EXPECT_GT(0.0f, scroll_event->y_offset()); + EXPECT_LT(0.0f, scroll_event->x_offset_ordinal()); + EXPECT_GT(0.0f, scroll_event->y_offset_ordinal()); + // Horizontal offset should be larger than vertical one, given the scroll + // offset in each direction. + EXPECT_GT(std::abs(scroll_event->x_offset()), + std::abs(scroll_event->y_offset())); + EXPECT_GT(std::abs(scroll_event->x_offset_ordinal()), + std::abs(scroll_event->y_offset_ordinal())); +} + INSTANTIATE_TEST_SUITE_P(XdgVersionStableTest, WaylandPointerTest, ::testing::Values(kXdgShellStable)); diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_popup.cc b/chromium/ui/ozone/platform/wayland/host/wayland_popup.cc index f1f7c8d43e5..d4d60cb9d41 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_popup.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_popup.cc @@ -19,9 +19,6 @@ WaylandPopup::WaylandPopup(PlatformWindowDelegate* delegate, WaylandPopup::~WaylandPopup() = default; bool WaylandPopup::CreateShellPopup() { - if (GetBounds().IsEmpty()) - return false; - DCHECK(parent_window() && !shell_popup_); auto subsurface_bounds_dip = @@ -131,15 +128,8 @@ void WaylandPopup::OnCloseRequest() { } bool WaylandPopup::OnInitialize(PlatformWindowInitProperties properties) { - if (!wl::IsMenuType(type())) - return false; - - set_parent_window(GetParentWindow(properties.parent_widget)); - if (!parent_window()) { - LOG(ERROR) << "Failed to get a parent window for this popup"; - return false; - } - // If parent window is known in advanced, we may set the scale early. + DCHECK(wl::IsMenuType(type())); + DCHECK(parent_window()); root_surface()->SetBufferScale(parent_window()->buffer_scale(), false); set_ui_scale(parent_window()->ui_scale()); return true; diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_screen.cc b/chromium/ui/ozone/platform/wayland/host/wayland_screen.cc index fc4d4fac5ca..9beaf27f2a5 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_screen.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_screen.cc @@ -8,13 +8,18 @@ #include <vector> #include "base/stl_util.h" +#include "build/build_config.h" +#include "build/chromeos_buildflags.h" #include "ui/display/display.h" #include "ui/display/display_finder.h" #include "ui/display/display_list.h" #include "ui/display/display_observer.h" +#include "ui/gfx/buffer_types.h" +#include "ui/gfx/display_color_spaces.h" #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/native_widget_types.h" +#include "ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" #include "ui/ozone/platform/wayland/host/wayland_cursor_position.h" #include "ui/ozone/platform/wayland/host/wayland_window.h" @@ -24,6 +29,45 @@ namespace ui { WaylandScreen::WaylandScreen(WaylandConnection* connection) : connection_(connection), weak_factory_(this) { DCHECK(connection_); + + // Chromium specifies either RGBA_8888 or BGRA_8888 as initial image format + // for alpha case and RGBX_8888 for no alpha case. Figure out + // which one is supported and use that. If RGBX_8888 is not supported, the + // format that |have_format_alpha| uses will be used by default (RGBA_8888 or + // BGRA_8888). + auto buffer_formats = + connection_->buffer_manager_host()->GetSupportedBufferFormats(); + for (const auto& buffer_format : buffer_formats) { + auto format = buffer_format.first; + + // TODO(crbug.com/1127822): Investigate a better fix for this. +#if !BUILDFLAG(IS_LACROS) + // RGBA_8888 is the preferred format, except when running on ChromiumOS. See + // crbug.com/1127558. + if (format == gfx::BufferFormat::RGBA_8888) + image_format_alpha_ = gfx::BufferFormat::RGBA_8888; + + // TODO(1128997): |image_format_no_alpha_| should use RGBX_8888 when it's + // available, but for some reason Chromium gets broken when it's used. + // Though, we can import RGBX_8888 dma buffer to EGLImage successfully. + // Enable that back when the issue is resolved. +#endif // !BUILDFLAG(IS_LACROS) + + if (!image_format_alpha_ && format == gfx::BufferFormat::BGRA_8888) + image_format_alpha_ = gfx::BufferFormat::BGRA_8888; + + if (image_format_alpha_ && image_format_no_alpha_) + break; + } + + // If no buffer formats are found (neither wl_drm nor zwp_linux_dmabuf are + // supported or the system has very limited set of supported buffer formats), + // RGBA_8888 is used by default. On Wayland, that seems to be the most + // supported. + if (!image_format_alpha_) + image_format_alpha_ = gfx::BufferFormat::RGBA_8888; + if (!image_format_no_alpha_) + image_format_no_alpha_ = image_format_alpha_; } WaylandScreen::~WaylandScreen() = default; @@ -61,12 +105,17 @@ void WaylandScreen::AddOrUpdateDisplay(uint32_t output_id, changed_display.set_bounds(new_bounds); changed_display.set_work_area(new_bounds); + gfx::DisplayColorSpaces color_spaces; + color_spaces.SetOutputBufferFormats(image_format_no_alpha_.value(), + image_format_alpha_.value()); + changed_display.set_color_spaces(color_spaces); + // There are 2 cases where |changed_display| must be set as primary: // 1. When it is the first one being added to the |display_list_|. Or - // 2. If it is nearest the origin than the previous primary or has the same - // origin as it. When an user, for example, swaps two side-by-side displays, - // at some point, as the notification come in, both will have the same - // origin. + // 2. If it is nearest the origin than the previous primary or has the + // same origin as it. When an user, for example, swaps two side-by-side + // displays, at some point, as the notification come in, both will have + // the same origin. auto type = display::DisplayList::Type::NOT_PRIMARY; if (display_list_.displays().empty()) { type = display::DisplayList::Type::PRIMARY; @@ -109,14 +158,14 @@ display::Display WaylandScreen::GetDisplayForAcceleratedWidget( const auto entered_outputs_ids = window->entered_outputs_ids(); // Although spec says a surface receives enter/leave surface events on // create/move/resize actions, this might be called right after a window is - // created, but it has not been configured by a Wayland compositor and it has - // not received enter surface events yet. Another case is when a user switches - // between displays in a single output mode - Wayland may not send enter - // events immediately, which can result in empty container of entered ids - // (check comments in WaylandWindow::RemoveEnteredOutputId). In this case, - // it's also safe to return the primary display. - // A child window will most probably enter the same display than its parent - // so we return the parent's display if there is a parent. + // created, but it has not been configured by a Wayland compositor and it + // has not received enter surface events yet. Another case is when a user + // switches between displays in a single output mode - Wayland may not send + // enter events immediately, which can result in empty container of entered + // ids (check comments in WaylandWindow::RemoveEnteredOutputId). In this + // case, it's also safe to return the primary display. A child window will + // most probably enter the same display than its parent so we return the + // parent's display if there is a parent. if (entered_outputs_ids.empty()) { if (parent_window) return GetDisplayForAcceleratedWidget(parent_window->GetWidget()); @@ -125,9 +174,9 @@ display::Display WaylandScreen::GetDisplayForAcceleratedWidget( DCHECK(!display_list_.displays().empty()); - // A widget can be located on two or more displays. It would be better if the - // most in DIP occupied display was returned, but it's impossible to do so in - // Wayland. Thus, return the one that was used the earliest. + // A widget can be located on two or more displays. It would be better if + // the most in DIP occupied display was returned, but it's impossible to do + // so in Wayland. Thus, return the one that was used the earliest. for (const auto& display : display_list_.displays()) { if (display.id() == *entered_outputs_ids.begin()) return display; @@ -140,13 +189,13 @@ display::Display WaylandScreen::GetDisplayForAcceleratedWidget( gfx::Point WaylandScreen::GetCursorScreenPoint() const { // Wayland does not provide either location of surfaces in global space // coordinate system or location of a pointer. Instead, only locations of - // mouse/touch events are known. Given that Chromium assumes top-level windows - // are located at origin, always provide a cursor point in regards to - // surfaces' location. + // mouse/touch events are known. Given that Chromium assumes top-level + // windows are located at origin, always provide a cursor point in regards + // to surfaces' location. // - // If a pointer is located in any of the existing wayland windows, return the - // last known cursor position. Otherwise, return such a point, which is not - // contained by any of the windows. + // If a pointer is located in any of the existing wayland windows, return + // the last known cursor position. Otherwise, return such a point, which is + // not contained by any of the windows. auto* cursor_position = connection_->wayland_cursor_position(); if (connection_->wayland_window_manager()->GetCurrentFocusedWindow() && cursor_position) diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_screen.h b/chromium/ui/ozone/platform/wayland/host/wayland_screen.h index 55993fc2a1a..a8dced4e2db 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_screen.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_screen.h @@ -10,7 +10,9 @@ #include "base/memory/weak_ptr.h" #include "base/observer_list.h" +#include "base/optional.h" #include "ui/display/display_list.h" +#include "ui/gfx/buffer_types.h" #include "ui/gfx/geometry/point.h" #include "ui/ozone/public/platform_screen.h" @@ -66,6 +68,9 @@ class WaylandScreen : public PlatformScreen { base::ObserverList<display::DisplayObserver> observers_; + base::Optional<gfx::BufferFormat> image_format_alpha_; + base::Optional<gfx::BufferFormat> image_format_no_alpha_; + base::WeakPtrFactory<WaylandScreen> weak_factory_; }; diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_shm_buffer.h b/chromium/ui/ozone/platform/wayland/host/wayland_shm_buffer.h index a33bf7d1549..1a1a958b093 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_shm_buffer.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_shm_buffer.h @@ -5,8 +5,6 @@ #ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_SHM_BUFFER_H_ #define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_SHM_BUFFER_H_ -#include <wayland-client.h> - #include <memory> #include "base/macros.h" diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_subsurface.cc b/chromium/ui/ozone/platform/wayland/host/wayland_subsurface.cc index 11d37ab75e1..9b27f61778d 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_subsurface.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_subsurface.cc @@ -4,7 +4,6 @@ #include "ui/ozone/platform/wayland/host/wayland_subsurface.h" -#include <wayland-client.h> #include <cstdint> #include "ui/ozone/platform/wayland/common/wayland_util.h" @@ -44,6 +43,7 @@ WaylandSubsurface::WaylandSubsurface(WaylandConnection* connection, LOG(ERROR) << "Failed to create wl_surface"; return; } + wayland_surface_.Initialize(); } WaylandSubsurface::~WaylandSubsurface() = default; @@ -70,11 +70,9 @@ bool WaylandSubsurface::IsVisible() const { } void WaylandSubsurface::UpdateOpaqueRegion() { - gfx::Size region_size = enable_blend_ ? gfx::Size() : bounds_px_.size(); - wl::Object<wl_region> region( - wl_compositor_create_region(connection_->compositor())); - wl_region_add(region.get(), 0, 0, region_size.width(), region_size.height()); - wl_surface_set_opaque_region(surface(), region.get()); + gfx::Rect region_px = + enable_blend_ ? gfx::Rect() : gfx::Rect(bounds_px_.size()); + wayland_surface()->SetOpaqueRegion(region_px); } void WaylandSubsurface::SetBounds(const gfx::Rect& bounds) { @@ -116,21 +114,22 @@ void WaylandSubsurface::CreateSubsurface() { wl_compositor_create_region(connection_->compositor())); wl_region_add(region.get(), 0, 0, 0, 0); wl_surface_set_input_region(surface(), region.get()); + + connection_->buffer_manager_host()->SetSurfaceConfigured(wayland_surface()); } void WaylandSubsurface::ConfigureAndShowSurface( gfx::OverlayTransform transform, + const gfx::RectF& src_rect, const gfx::Rect& bounds_rect, bool enable_blend, const WaylandSurface* reference_below, const WaylandSurface* reference_above) { + wayland_surface()->SetBufferTransform(transform); wayland_surface()->SetBufferScale(parent_->buffer_scale(), false); - gfx::Rect bounds_px{ - bounds_rect.origin() + parent_->GetBounds().origin().OffsetFromOrigin(), - bounds_rect.size()}; auto old_bounds = bounds_px_; - SetBounds(bounds_px); + SetBounds(bounds_rect); if (old_bounds != bounds_px_ || enable_blend_ != enable_blend) { enable_blend_ = enable_blend; @@ -145,6 +144,9 @@ void WaylandSubsurface::ConfigureAndShowSurface( } else if (reference_above) { wl_subsurface_place_below(subsurface_.get(), reference_above->surface()); } + + wayland_surface()->SetViewportSource(src_rect); + wayland_surface()->SetViewportDestination(bounds_rect.size()); } } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_subsurface.h b/chromium/ui/ozone/platform/wayland/host/wayland_subsurface.h index 50098b21715..26a4c589bee 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_subsurface.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_subsurface.h @@ -34,7 +34,23 @@ class WaylandSubsurface { // Sets up wl_surface and wl_subsurface. Allows an overlay to be shown // correctly once a wl_buffer is attached. + // |transform|: specifies the wl_surface buffer_transform. + // |src_rect|: specifies the displayable content (wp_viewport.src) of + // upcoming attached buffers. + // |bounds_rect|: The contents of the source rectangle are scaled to the + // destination size (wp_viewport.dst). + // |enable_blend|: whether the wl_surface will be transluscent. + // |reference_below| & |reference_above|: this subsurface is taken from the + // subsurface stack and inserted back to be immediately below/above the + // reference subsurface. + // + // The coordinate transformations from buffer pixel coordinates up to the + // surface-local coordinates happen in the following order: + // 1. buffer_transform + // 2. buffer_scale + // 3. crop and scale of viewport void ConfigureAndShowSurface(gfx::OverlayTransform transform, + const gfx::RectF& src_rect, const gfx::Rect& bounds_rect, bool enable_blend, const WaylandSurface* reference_below, diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_surface.cc b/chromium/ui/ozone/platform/wayland/host/wayland_surface.cc index 0300a7bcacc..3cfccaa7138 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_surface.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_surface.cc @@ -4,6 +4,11 @@ #include "ui/ozone/platform/wayland/host/wayland_surface.h" +#include <viewporter-client-protocol.h> + +#include "ui/gfx/geometry/rect_conversions.h" +#include "ui/gfx/native_widget_types.h" +#include "ui/ozone/platform/wayland/common/wayland_util.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" #include "ui/ozone/platform/wayland/host/wayland_window.h" @@ -15,12 +20,7 @@ WaylandSurface::WaylandSurface(WaylandConnection* connection, root_window_(root_window), surface_(connection->CreateSurface()) {} -WaylandSurface::~WaylandSurface() { - if (surface_) { - wl_surface_add_listener(surface_.get(), nullptr, nullptr); - wl_surface_set_user_data(surface_.get(), nullptr); - } -} +WaylandSurface::~WaylandSurface() = default; uint32_t WaylandSurface::GetSurfaceId() const { if (!surface_) @@ -29,24 +29,36 @@ uint32_t WaylandSurface::GetSurfaceId() const { } gfx::AcceleratedWidget WaylandSurface::GetWidget() const { - return root_window_->GetWidget(); + return root_window_ ? root_window_->GetWidget() : gfx::kNullAcceleratedWidget; } bool WaylandSurface::Initialize() { if (!surface_) return false; - wl_surface_set_user_data(surface_.get(), this); - static struct wl_surface_listener surface_listener = { &WaylandSurface::Enter, &WaylandSurface::Leave, }; wl_surface_add_listener(surface_.get(), &surface_listener, this); + if (connection_->viewporter()) { + viewport_.reset( + wp_viewporter_get_viewport(connection_->viewporter(), surface())); + if (!viewport_) { + LOG(ERROR) << "Failed to create wp_viewport"; + return false; + } + } + return true; } +void WaylandSurface::UnsetRootWindow() { + DCHECK(surface_); + root_window_ = nullptr; +} + void WaylandSurface::AttachBuffer(wl_buffer* buffer) { // The logic in DamageBuffer currently relies on attachment coordinates of // (0, 0). If this changes, then the calculation in DamageBuffer will also @@ -55,7 +67,39 @@ void WaylandSurface::AttachBuffer(wl_buffer* buffer) { connection_->ScheduleFlush(); } -void WaylandSurface::Damage(const gfx::Rect& pending_damage_region) { +void WaylandSurface::UpdateBufferDamageRegion( + const gfx::Rect& pending_damage_region, + const gfx::Size& buffer_size) { + // Buffer-local coordinates are in pixels, surface coordinates are in DIP. + // The coordinate transformations from buffer pixel coordinates up to + // the surface-local coordinates happen in the following order: + // 1. buffer_transform (wl_surface.set_buffer_transform) + // 2. buffer_scale (wl_surface.set_buffer_scale) + // 3. crop and scale (wp_viewport.set*) + // Apply buffer_transform (wl_surface.set_buffer_transform). + gfx::Size bounds = wl::ApplyWaylandTransform( + buffer_size, wl::ToWaylandTransform(buffer_transform_)); + // Apply buffer_scale (wl_surface.set_buffer_scale). + bounds = gfx::ScaleToCeiledSize(bounds, 1.f / buffer_scale_); + // Apply crop (wp_viewport.set_source). + gfx::Rect viewport_src = gfx::Rect(bounds); + if (!crop_rect_.IsEmpty()) { + viewport_src = gfx::ToEnclosedRect( + gfx::ScaleRect(crop_rect_, bounds.width(), bounds.height())); + wp_viewport_set_source(viewport(), wl_fixed_from_int(viewport_src.x()), + wl_fixed_from_int(viewport_src.y()), + wl_fixed_from_int(viewport_src.width()), + wl_fixed_from_int(viewport_src.height())); + } + // Apply viewport scale (wp_viewport.set_destination). + gfx::Size viewport_dst = bounds; + if (!display_size_px_.IsEmpty()) { + viewport_dst = + gfx::ScaleToCeiledSize(display_size_px_, 1.f / buffer_scale_); + wp_viewport_set_destination(viewport(), viewport_dst.width(), + viewport_dst.height()); + } + if (connection_->compositor_version() >= WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION) { // wl_surface_damage_buffer relies on compositor API version 4. See @@ -66,20 +110,29 @@ void WaylandSurface::Damage(const gfx::Rect& pending_damage_region) { surface_.get(), pending_damage_region.x(), pending_damage_region.y(), pending_damage_region.width(), pending_damage_region.height()); } else { - // The calculation for damage region relies on two assumptions: - // 1) The buffer is always attached at surface location (0, 0) - // 2) The API wl_surface::set_buffer_transform is not used. - // It's possible to write logic that accounts for both cases above, but - // it's currently unnecessary. - // - // Note: The damage region may not be an integer multiple of scale. To - // keep the implementation simple, the x() and y() coordinates round down, - // and the width() and height() calculations always add an extra pixel. - wl_surface_damage(surface_.get(), pending_damage_region.x() / buffer_scale_, - pending_damage_region.y() / buffer_scale_, - pending_damage_region.width() / buffer_scale_ + 1, - pending_damage_region.height() / buffer_scale_ + 1); + // Calculate the damage region in surface coordinates. + // The calculation for damage region relies on the assumption: The buffer is + // always attached at surface location (0, 0). + // It's possible to write logic that accounts for attaching buffer at other + // locations, but it's currently unnecessary. + + // Apply buffer_transform (wl_surface.set_buffer_transform). + gfx::Rect damage = + wl::ApplyWaylandTransform(pending_damage_region, buffer_size, + wl::ToWaylandTransform(buffer_transform_)); + // Apply buffer_scale (wl_surface.set_buffer_scale). + damage = gfx::ScaleToEnclosingRect(damage, 1.f / buffer_scale_); + // Adjust coordinates to |viewport_src| (wp_viewport.set_source). + damage = wl::TranslateBoundsToParentCoordinates(damage, viewport_src); + // Apply viewport scale (wp_viewport.set_destination). + damage = gfx::ScaleToEnclosingRect( + damage, static_cast<float>(viewport_dst.width()) / viewport_src.width(), + static_cast<float>(viewport_dst.height()) / viewport_src.height()); + + wl_surface_damage(surface_.get(), damage.x(), damage.y(), damage.width(), + damage.height()); } + connection_->ScheduleFlush(); } @@ -88,6 +141,16 @@ void WaylandSurface::Commit() { connection_->ScheduleFlush(); } +void WaylandSurface::SetBufferTransform(gfx::OverlayTransform transform) { + DCHECK(transform != gfx::OVERLAY_TRANSFORM_INVALID); + if (buffer_transform_ == transform) + return; + + buffer_transform_ = transform; + wl_output_transform wl_transform = wl::ToWaylandTransform(buffer_transform_); + wl_surface_set_buffer_transform(surface_.get(), wl_transform); +} + void WaylandSurface::SetBufferScale(int32_t new_scale, bool update_bounds) { DCHECK_GT(new_scale, 0); @@ -99,21 +162,46 @@ void WaylandSurface::SetBufferScale(int32_t new_scale, bool update_bounds) { connection_->ScheduleFlush(); } -void WaylandSurface::SetBounds(const gfx::Rect& bounds_px) { +void WaylandSurface::SetOpaqueRegion(const gfx::Rect& region_px) { // It's important to set opaque region for opaque windows (provides // optimization hint for the Wayland compositor). - if (!root_window_->IsOpaqueWindow()) + if (!root_window_ || !root_window_->IsOpaqueWindow()) return; wl::Object<wl_region> region( wl_compositor_create_region(connection_->compositor())); - wl_region_add(region.get(), 0, 0, bounds_px.width(), bounds_px.height()); + gfx::Rect region_dip = + gfx::ScaleToEnclosingRect(region_px, 1.f / buffer_scale_); + wl_region_add(region.get(), region_dip.x(), region_dip.y(), + region_dip.width(), region_dip.height()); wl_surface_set_opaque_region(surface_.get(), region.get()); connection_->ScheduleFlush(); } +void WaylandSurface::SetViewportSource(const gfx::RectF& src_rect) { + if (src_rect == crop_rect_) { + return; + } else if (src_rect.IsEmpty() || src_rect == gfx::RectF{0.f, 0.f, 1.f, 1.f}) { + wp_viewport_set_source(viewport(), wl_fixed_from_int(-1), + wl_fixed_from_int(-1), wl_fixed_from_int(-1), + wl_fixed_from_int(-1)); + return; + } + + crop_rect_ = src_rect; +} + +void WaylandSurface::SetViewportDestination(const gfx::Size& dest_size_px) { + if (dest_size_px == display_size_px_) { + return; + } else if (dest_size_px.IsEmpty()) { + wp_viewport_set_destination(viewport(), -1, -1); + } + display_size_px_ = dest_size_px; +} + wl::Object<wl_subsurface> WaylandSurface::CreateSubsurface( WaylandSurface* parent) { DCHECK(parent); @@ -128,15 +216,16 @@ wl::Object<wl_subsurface> WaylandSurface::CreateSubsurface( void WaylandSurface::Enter(void* data, struct wl_surface* wl_surface, struct wl_output* output) { - static_cast<WaylandSurface*>(data)->root_window_->AddEnteredOutputId(output); + if (auto* root_window = static_cast<WaylandSurface*>(data)->root_window_) + root_window->AddEnteredOutputId(output); } // static void WaylandSurface::Leave(void* data, struct wl_surface* wl_surface, struct wl_output* output) { - static_cast<WaylandSurface*>(data)->root_window_->RemoveEnteredOutputId( - output); + if (auto* root_window = static_cast<WaylandSurface*>(data)->root_window_) + root_window->RemoveEnteredOutputId(output); } } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_surface.h b/chromium/ui/ozone/platform/wayland/host/wayland_surface.h index 37755f9ac3d..7747c6e9250 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_surface.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_surface.h @@ -8,7 +8,9 @@ #include <cstdint> #include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/rect_f.h" #include "ui/gfx/native_widget_types.h" +#include "ui/gfx/overlay_transform.h" #include "ui/ozone/platform/wayland/common/wayland_object.h" namespace ui { @@ -26,6 +28,7 @@ class WaylandSurface { WaylandWindow* root_window() const { return root_window_; } wl_surface* surface() const { return surface_.get(); } + wp_viewport* viewport() const { return viewport_.get(); } int32_t buffer_scale() const { return buffer_scale_; } void set_buffer_scale(int32_t scale) { buffer_scale_ = scale; } @@ -39,22 +42,43 @@ class WaylandSurface { // This may return false if a wl_surface could not be created, for example. bool Initialize(); + // Unsets |root_window_|. This is intended to be used in special cases, where + // the underlying wl_surface must be kept alive with no root window associated + // (e.g: window/tab dragging sessions). + void UnsetRootWindow(); + // Attaches the given wl_buffer to the underlying wl_surface at (0, 0). void AttachBuffer(wl_buffer* buffer); - // Damages the surface according to |pending_damage_region|, which should be - // in surface coordinates (dp). - void Damage(const gfx::Rect& pending_damage_region); + // Describes where the surface needs to be repainted according to + // |buffer_pending_damage_region|, which should be in buffer coordinates (px). + void UpdateBufferDamageRegion(const gfx::Rect& buffer_pending_damage_region, + const gfx::Size& buffer_size); // Commits the underlying wl_surface. void Commit(); + // Sets an optional transformation for how the Wayland compositor interprets + // the contents of the buffer attached to this surface. + void SetBufferTransform(gfx::OverlayTransform transform); + // Sets the buffer scale for this surface. void SetBufferScale(int32_t scale, bool update_bounds); - // Sets the bounds on this surface. This is used for determining the opaque - // region. - void SetBounds(const gfx::Rect& bounds_px); + // Sets the region that is opaque on this surface in physical pixels. This is + // expected to be called whenever the region that the surface span changes or + // the opacity changes. + void SetOpaqueRegion(const gfx::Rect& bounds_px); + + // Set the source rectangle of the associated wl_surface. + // See: + // https://cgit.freedesktop.org/wayland/wayland-protocols/tree/stable/viewporter/viewporter.xml + // If |src_rect| is empty, the source rectangle is unset. + void SetViewportSource(const gfx::RectF& src_rect); + + // Set the destination size of the associated wl_surface according to + // |dest_size_px|, which should be in physical pixels. + void SetViewportDestination(const gfx::Size& dest_size_px); // Creates a wl_subsurface relating this surface and a parent surface, // |parent|. Callers take ownership of the wl_subsurface. @@ -62,13 +86,30 @@ class WaylandSurface { private: WaylandConnection* const connection_; - WaylandWindow* const root_window_; + WaylandWindow* root_window_ = nullptr; wl::Object<wl_surface> surface_; + wl::Object<wp_viewport> viewport_; + + // Transformation for how the compositor interprets the contents of the + // buffer. + gfx::OverlayTransform buffer_transform_ = gfx::OVERLAY_TRANSFORM_NONE; - // Wayland's scale factor for the output that this window currently belongs + // Wayland's scale factor for the output that this surface currently belongs // to. int32_t buffer_scale_ = 1; + // Following fields are used to help determine the damage_region in + // surface-local coordinates if wl_surface_damage_buffer() is not available. + // Normalized bounds of the buffer to be displayed in |display_size_px_|. + // If empty, no cropping is applied. + gfx::RectF crop_rect_ = gfx::RectF(); + + // Current size of the destination of the viewport in physical pixels. Wayland + // compositor will scale the (cropped) buffer content to fit the + // |display_size_px_|. + // If empty, no scaling is applied. + gfx::Size display_size_px_ = gfx::Size(); + // wl_surface_listener static void Enter(void* data, struct wl_surface* wl_surface, diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc b/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc index 86bab661269..0af423cb801 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc @@ -4,9 +4,11 @@ #include "ui/ozone/platform/wayland/host/wayland_toplevel_window.h" +#include <aura-shell-client-protocol.h> + #include "base/run_loop.h" #include "base/unguessable_token.h" -#include "build/lacros_buildflags.h" +#include "build/chromeos_buildflags.h" #include "ui/base/dragdrop/drag_drop_types.h" #include "ui/base/dragdrop/os_exchange_data.h" #include "ui/base/hit_test.h" @@ -22,6 +24,12 @@ #include "ui/platform_window/extensions/wayland_extension.h" #include "ui/platform_window/wm/wm_drop_handler.h" +#if BUILDFLAG(IS_LACROS) +// TODO(jamescook): The nogncheck is to work around false-positive failures on +// the code search bot. Remove after https://crrev.com/c/2432137 lands. +#include "chromeos/crosapi/cpp/crosapi_constants.h" // nogncheck +#endif + namespace ui { WaylandToplevelWindow::WaylandToplevelWindow(PlatformWindowDelegate* delegate, @@ -336,15 +344,21 @@ bool WaylandToplevelWindow::OnInitialize( PlatformWindowInitProperties properties) { #if BUILDFLAG(IS_LACROS) auto token = base::UnguessableToken::Create(); - window_unique_id_ = "org.chromium.lacros." + token.ToString(); + window_unique_id_ = + std::string(crosapi::kLacrosAppIdPrefix) + token.ToString(); #else wm_class_class_ = properties.wm_class_class; #endif SetWaylandExtension(this, static_cast<WaylandExtension*>(this)); SetWmMoveLoopHandler(this, static_cast<WmMoveLoopHandler*>(this)); + InitializeAuraShell(); return true; } +bool WaylandToplevelWindow::IsActive() const { + return is_active_; +} + bool WaylandToplevelWindow::RunMoveLoop(const gfx::Vector2d& drag_offset) { DCHECK(connection()->window_drag_controller()); return connection()->window_drag_controller()->Drag(this, drag_offset); @@ -416,4 +430,14 @@ void WaylandToplevelWindow::SetOrResetRestoredBounds() { } } +void WaylandToplevelWindow::InitializeAuraShell() { + if (connection()->aura_shell()) { + DCHECK(!aura_surface_); + aura_surface_.reset(zaura_shell_get_aura_surface( + connection()->aura_shell(), root_surface()->surface())); + zaura_surface_set_fullscreen_mode(aura_surface_.get(), + ZAURA_SURFACE_FULLSCREEN_MODE_IMMERSIVE); + } +} + } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.h b/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.h index 4c0a6a7d013..7f091d3f48f 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.h @@ -5,7 +5,7 @@ #ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_TOPLEVEL_WINDOW_H_ #define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_TOPLEVEL_WINDOW_H_ -#include "build/lacros_buildflags.h" +#include "build/chromeos_buildflags.h" #include "ui/gfx/geometry/vector2d.h" #include "ui/ozone/platform/wayland/host/wayland_window.h" #include "ui/platform_window/extensions/wayland_extension.h" @@ -76,6 +76,7 @@ class WaylandToplevelWindow : public WaylandWindow, void OnDragLeave() override; void OnDragSessionClose(uint32_t dnd_action) override; bool OnInitialize(PlatformWindowInitProperties properties) override; + bool IsActive() const override; // WmMoveLoopHandler: bool RunMoveLoop(const gfx::Vector2d& drag_offset) override; @@ -97,6 +98,9 @@ class WaylandToplevelWindow : public WaylandWindow, void SetOrResetRestoredBounds(); + // Initializes the aura-shell EXO extension, if available. + void InitializeAuraShell(); + // Wrappers around shell surface. std::unique_ptr<ShellSurfaceWrapper> shell_surface_; @@ -142,6 +146,8 @@ class WaylandToplevelWindow : public WaylandWindow, base::OnceClosure drag_loop_quit_closure_; + wl::Object<zaura_surface> aura_surface_; + base::WeakPtrFactory<WaylandToplevelWindow> weak_ptr_factory_{this}; }; diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_touch.cc b/chromium/ui/ozone/platform/wayland/host/wayland_touch.cc index 9298a43d67d..6bb9802ed9a 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_touch.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_touch.cc @@ -4,9 +4,8 @@ #include "ui/ozone/platform/wayland/host/wayland_touch.h" -#include <wayland-client.h> - #include "base/time/time.h" +#include "ui/events/types/event_type.h" #include "ui/gfx/geometry/point_f.h" #include "ui/ozone/platform/wayland/common/wayland_util.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" @@ -46,7 +45,7 @@ void WaylandTouch::Down(void* data, WaylandTouch* touch = static_cast<WaylandTouch*>(data); DCHECK(touch); - touch->connection_->set_serial(serial); + touch->connection_->set_serial(serial, ET_TOUCH_PRESSED); WaylandWindow* window = wl::RootWindowFromWlSurface(surface); gfx::PointF location(wl_fixed_to_double(x), wl_fixed_to_double(y)); @@ -63,6 +62,8 @@ void WaylandTouch::Up(void* data, WaylandTouch* touch = static_cast<WaylandTouch*>(data); DCHECK(touch); + touch->connection_->set_serial(serial, ET_TOUCH_RELEASED); + base::TimeTicks timestamp = base::TimeTicks() + base::TimeDelta::FromMilliseconds(time); touch->delegate_->OnTouchReleaseEvent(timestamp, id); diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window.cc b/chromium/ui/ozone/platform/wayland/host/wayland_window.cc index 2e436462afb..f7e6ae5d8dd 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_window.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window.cc @@ -4,8 +4,6 @@ #include "ui/ozone/platform/wayland/host/wayland_window.h" -#include <wayland-client.h> - #include <algorithm> #include <memory> @@ -136,7 +134,7 @@ void WaylandWindow::SetBounds(const gfx::Rect& bounds_px) { return; bounds_px_ = bounds_px; - root_surface_->SetBounds(bounds_px); + root_surface_->SetOpaqueRegion(bounds_px); delegate_->OnBoundsChanged(bounds_px_); } @@ -255,6 +253,8 @@ bool WaylandWindow::CanDispatchEvent(const PlatformEvent& event) { return has_keyboard_focus_; if (event->IsTouchEvent()) return has_touch_focus_; + if (event->IsScrollEvent()) + return has_pointer_focus_; return false; } @@ -358,33 +358,11 @@ bool WaylandWindow::Initialize(PlatformWindowInitProperties properties) { // Will do nothing for menus because they have got their scale above. UpdateBufferScale(false); - root_surface_->SetBounds(bounds_px_); + root_surface_->SetOpaqueRegion(bounds_px_); return true; } -WaylandWindow* WaylandWindow::GetParentWindow( - gfx::AcceleratedWidget parent_widget) { - auto* parent_window = - connection_->wayland_window_manager()->GetWindow(parent_widget); - - // If propagated parent has already had a child, it means that |this| is a - // submenu of a 3-dot menu. In aura, the parent of a 3-dot menu and its - // submenu is the main native widget, which is the main window. In contrast, - // Wayland requires a menu window to be a parent of a submenu window. Thus, - // check if the suggested parent has a child. If yes, take its child as a - // parent of |this|. - // Another case is a notifcation window or a drop down window, which do not - // have a parent in aura. In this case, take the current focused window as a - // parent. - - if (!parent_window) - parent_window = - connection_->wayland_window_manager()->GetCurrentFocusedWindow(); - - return parent_window ? parent_window->GetTopMostChildWindow() : nullptr; -} - WaylandWindow* WaylandWindow::GetRootParentWindow() { return parent_window_ ? parent_window_->GetRootParentWindow() : this; } @@ -469,6 +447,11 @@ bool WaylandWindow::IsOpaqueWindow() const { return opacity_ == ui::PlatformWindowOpacity::kOpaqueWindow; } +bool WaylandWindow::IsActive() const { + // Please read the comment where the IsActive method is declared. + return false; +} + uint32_t WaylandWindow::DispatchEventToDelegate( const PlatformEvent& native_event) { auto* event = static_cast<Event*>(native_event); @@ -484,6 +467,7 @@ uint32_t WaylandWindow::DispatchEventToDelegate( std::unique_ptr<WaylandSurface> WaylandWindow::TakeWaylandSurface() { DCHECK(shutting_down_); DCHECK(root_surface_); + root_surface_->UnsetRootWindow(); return std::move(root_surface_); } @@ -540,8 +524,9 @@ bool WaylandWindow::CommitOverlays( ozone::mojom::WaylandOverlayConfig::New(); auto split = std::lower_bound(overlays.begin(), overlays.end(), value, OverlayStackOrderCompare); - CHECK((*split)->z_order >= 0); - size_t num_primary_planes = (*split)->z_order == 0 ? 1 : 0; + CHECK(split == overlays.end() || (*split)->z_order >= 0); + size_t num_primary_planes = + (split != overlays.end() && (*split)->z_order == 0) ? 1 : 0; size_t above = (overlays.end() - split) - num_primary_planes; size_t below = split - overlays.begin(); @@ -567,11 +552,12 @@ bool WaylandWindow::CommitOverlays( reference_above = (*std::next(iter))->wayland_surface(); } (*iter)->ConfigureAndShowSurface( - (*overlay_iter)->transform, (*overlay_iter)->bounds_rect, - (*overlay_iter)->enable_blend, nullptr, reference_above); + (*overlay_iter)->transform, (*overlay_iter)->crop_rect, + (*overlay_iter)->bounds_rect, (*overlay_iter)->enable_blend, + nullptr, reference_above); connection_->buffer_manager_host()->CommitBufferInternal( - (*iter)->wayland_surface(), (*overlay_iter)->buffer_id, - gfx::Rect()); + (*iter)->wayland_surface(), (*overlay_iter)->buffer_id, gfx::Rect(), + /*wait_for_frame_callback=*/false); } else { // If there're more subsurfaces requested that we don't need at the // moment, hide them. @@ -595,11 +581,12 @@ bool WaylandWindow::CommitOverlays( reference_below = (*std::prev(iter))->wayland_surface(); } (*iter)->ConfigureAndShowSurface( - (*overlay_iter)->transform, (*overlay_iter)->bounds_rect, - (*overlay_iter)->enable_blend, reference_below, nullptr); + (*overlay_iter)->transform, (*overlay_iter)->crop_rect, + (*overlay_iter)->bounds_rect, (*overlay_iter)->enable_blend, + reference_below, nullptr); connection_->buffer_manager_host()->CommitBufferInternal( - (*iter)->wayland_surface(), (*overlay_iter)->buffer_id, - gfx::Rect()); + (*iter)->wayland_surface(), (*overlay_iter)->buffer_id, gfx::Rect(), + /*wait_for_frame_callback=*/false); } else { // If there're more subsurfaces requested that we don't need at the // moment, hide them. @@ -609,16 +596,16 @@ bool WaylandWindow::CommitOverlays( } if (num_primary_planes) { - // TODO: forward fence. connection_->buffer_manager_host()->CommitBufferInternal( - root_surface(), (*split)->buffer_id, (*split)->damage_region); + root_surface(), (*split)->buffer_id, (*split)->damage_region, + /*wait_for_frame_callback=*/true); } else { - // Subsurfaces are set to desync, above operations will only take effects + // Subsurfaces are set to sync, above operations will only take effects // when root_surface is committed. - root_surface()->Commit(); + connection_->buffer_manager_host()->CommitWithoutBufferInternal( + root_surface(), /*wait_for_frame_callback=*/true); } - // commit all; return true; } diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window.h b/chromium/ui/ozone/platform/wayland/host/wayland_window.h index cd9f1e9fb35..c341ddb148a 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_window.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window.h @@ -183,6 +183,11 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher { // Returns true iff this window is opaque. bool IsOpaqueWindow() const; + // Says if the current window is set as active by the Wayland server. This + // only applies to toplevel surfaces (surfaces such as popups, subsurfaces do + // not support that). + virtual bool IsActive() const; + protected: WaylandWindow(PlatformWindowDelegate* delegate, WaylandConnection* connection); @@ -193,9 +198,6 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher { // Sets bounds in dip. void SetBoundsDip(const gfx::Rect& bounds_dip); - // Gets a parent window for this window. - WaylandWindow* GetParentWindow(gfx::AcceleratedWidget parent_widget); - void set_ui_scale(int32_t ui_scale) { ui_scale_ = ui_scale; } private: diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc b/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc index 64ffd8b4845..548359854c5 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc @@ -155,6 +155,7 @@ void WaylandWindowDragController::OnDragEnter(WaylandWindow* window, // Forward focus change event to the input delegate, so other components, such // as WaylandScreen, are able to properly retrieve focus related info during // window dragging sesstions. + pointer_location_ = location; pointer_delegate_->OnPointerFocusChanged(window, location); VLOG(1) << "OnEnter. widget=" << window->GetWidget(); @@ -179,6 +180,7 @@ void WaylandWindowDragController::OnDragMotion(const gfx::PointF& location) { VLOG(2) << "OnMotion. location=" << location.ToString(); // Forward cursor location update info to the input handling delegate. + pointer_location_ = location; pointer_delegate_->OnPointerMotionEvent(location); } @@ -207,12 +209,15 @@ void WaylandWindowDragController::OnDragLeave() { // As Wayland clients are only aware of surface-local coordinates and there is // no implicit grab during DND sessions, a fake motion event with negative - // coordinates must be used here to make it possible for higher level UI - // components to detect when a window should be detached. E.g: On Chrome, - // dragging a tab all the way up to the top edge of the window won't work - // without this fake motion event upon wl_data_device::leave events. + // y coordinate is used here to allow higher level UI components to detect + // when a window should be detached. E.g: On Chrome, dragging a tab all the + // way up to the top edge of the window won't work without this fake motion + // event upon wl_data_device::leave events. This is a workaround and should + // ideally be reworked in the future, at higher level layers such that they + // properly handle platforms that do not support global screen coordinates, + // like Wayland. if (state_ == State::kAttached) - pointer_delegate_->OnPointerMotionEvent({-1, -1}); + pointer_delegate_->OnPointerMotionEvent({pointer_location_.x(), -1}); } void WaylandWindowDragController::OnDragDrop() { diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.h b/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.h index fa3ed531119..fcbfae4ad36 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.h @@ -113,6 +113,9 @@ class WaylandWindowDragController : public WaylandDataDevice::DragDelegate, State state_ = State::kIdle; gfx::Vector2d drag_offset_; + // The last known pointer location in DIP. + gfx::PointF pointer_location_; + std::unique_ptr<WaylandDataSource> data_source_; std::unique_ptr<WaylandDataOffer> data_offer_; diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller_unittest.cc b/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller_unittest.cc index 4f24aadffb4..9e6b6e29013 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller_unittest.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller_unittest.cc @@ -548,12 +548,16 @@ TEST_P(WaylandWindowDragControllerTest, DragExitAttached) { Sync(); EXPECT_EQ(State::kAttached, drag_controller()->state()); - // Emulate a wl_data_device::leave and make sure a motion event is dispatched - // in response. + // Emulate a [motion => leave] event sequence and make sure the correct + // ui::Events are dispatched in response. + SendDndMotion({50, 50}); + EXPECT_CALL(delegate_, DispatchEvent(_)).Times(1); + Sync(); + SendDndLeave(); EXPECT_CALL(delegate_, DispatchEvent(_)).WillOnce([&](Event* event) { EXPECT_EQ(ET_MOUSE_DRAGGED, event->type()); - EXPECT_EQ(gfx::Point(-1, -1).ToString(), + EXPECT_EQ(gfx::Point(50, -1).ToString(), event->AsMouseEvent()->location().ToString()); }); Sync(); diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_factory.cc b/chromium/ui/ozone/platform/wayland/host/wayland_window_factory.cc index 7854b05b999..7cca6ba6a82 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_window_factory.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_factory.cc @@ -22,28 +22,41 @@ std::unique_ptr<WaylandWindow> WaylandWindow::Create( switch (properties.type) { case PlatformWindowType::kMenu: case PlatformWindowType::kPopup: - // We are unable to create a popup or menu window, because they require a - // parent window to be set. Thus, create a normal window instead then. - if (properties.parent_widget == gfx::kNullAcceleratedWidget && - !connection->wayland_window_manager()->GetCurrentFocusedWindow()) { - window.reset(new WaylandToplevelWindow(delegate, connection)); - } else if (connection->IsDragInProgress()) { + if (connection->IsDragInProgress()) { // We are in the process of drag and requested a popup. Most probably, // it is an arrow window. - window.reset(new WaylandAuxiliaryWindow(delegate, connection)); + window = std::make_unique<WaylandAuxiliaryWindow>(delegate, connection); } else { - window.reset(new WaylandPopup(delegate, connection)); + auto* parent_window = + connection->wayland_window_manager()->FindParentForNewWindow( + properties.parent_widget); + if (parent_window) { + // Set the parent window in advance otherwise it is not possible to + // know if the WaylandPopup is able to find one and if + // WaylandWindow::Initialize() fails or not. Otherwise, + // WaylandWindow::Create() returns nullptr and makes the browser to + // fail. To fix this problem, search for the parent window and if + // one is not found, create WaylandToplevelWindow instead. It's + // also worth noting that searching twice (one time here and another + // by WaylandPopup) is a bad practice, and the parent window is set + // here instead. + window = std::make_unique<WaylandPopup>(delegate, connection); + window->set_parent_window(parent_window); + } else { + window = + std::make_unique<WaylandToplevelWindow>(delegate, connection); + } } break; case PlatformWindowType::kTooltip: - window.reset(new WaylandAuxiliaryWindow(delegate, connection)); + window = std::make_unique<WaylandAuxiliaryWindow>(delegate, connection); break; case PlatformWindowType::kWindow: case PlatformWindowType::kBubble: case PlatformWindowType::kDrag: // TODO(msisov): Figure out what kind of surface we need to create for // bubble and drag windows. - window.reset(new WaylandToplevelWindow(delegate, connection)); + window = std::make_unique<WaylandToplevelWindow>(delegate, connection); break; default: NOTREACHED(); diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_manager.cc b/chromium/ui/ozone/platform/wayland/host/wayland_window_manager.cc index 0c9b86e81fc..ac1549a20a8 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_window_manager.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_manager.cc @@ -82,6 +82,37 @@ WaylandWindow* WaylandWindowManager::GetCurrentKeyboardFocusedWindow() const { return nullptr; } +WaylandWindow* WaylandWindowManager::FindParentForNewWindow( + gfx::AcceleratedWidget parent_widget) const { + auto* parent_window = GetWindow(parent_widget); + + // If propagated parent has already had a child, it means that |this| is a + // submenu of a 3-dot menu. In aura, the parent of a 3-dot menu and its + // submenu is the main native widget, which is the main window. In contrast, + // Wayland requires a menu window to be a parent of a submenu window. Thus, + // check if the suggested parent has a child. If yes, take its child as a + // parent of |this|. + // Another case is a notification window or a drop down window, which does not + // have a parent in aura. In this case, take the current focused window as a + // parent. + if (!parent_window) + parent_window = GetCurrentFocusedWindow(); + + // If there is no current focused window, figure out the current active window + // set by the Wayland server. Only one window at a time can be set as active. + if (!parent_window) { + auto windows = GetAllWindows(); + for (auto* window : windows) { + if (window->IsActive()) { + parent_window = window; + break; + } + } + } + + return parent_window ? parent_window->GetTopMostChildWindow() : nullptr; +} + std::vector<WaylandWindow*> WaylandWindowManager::GetWindowsOnOutput( uint32_t output_id) { std::vector<WaylandWindow*> result; diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_manager.h b/chromium/ui/ozone/platform/wayland/host/wayland_window_manager.h index ca32c24833f..47abf725740 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_window_manager.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_manager.h @@ -10,6 +10,7 @@ #include "base/containers/flat_map.h" #include "base/macros.h" #include "base/observer_list.h" +#include "ui/gfx/geometry/size_f.h" #include "ui/gfx/native_widget_types.h" #include "ui/ozone/platform/wayland/host/wayland_window_observer.h" @@ -55,6 +56,13 @@ class WaylandWindowManager { // Returns a current focused window by keyboard. WaylandWindow* GetCurrentKeyboardFocusedWindow() const; + // Returns a parent window suitable for newly created non-toplevel windows. If + // the |parent_widget| is gfx::kNullAcceleratedWidget, either the currently + // focused or the active window is used. If the found parent has children + // windows, the one on top the of the stack is used as a parent. + WaylandWindow* FindParentForNewWindow( + gfx::AcceleratedWidget parent_widget) const; + // TODO(crbug.com/971525): remove this in favor of targeted subscription of // windows to their outputs. std::vector<WaylandWindow*> GetWindowsOnOutput(uint32_t output_id); diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_unittest.cc b/chromium/ui/ozone/platform/wayland/host/wayland_window_unittest.cc index 2626222a427..c081c9c3514 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_window_unittest.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_unittest.cc @@ -19,6 +19,7 @@ #include "ui/base/hit_test.h" #include "ui/events/base_event_utils.h" #include "ui/events/event.h" +#include "ui/gfx/native_widget_types.h" #include "ui/gfx/overlay_transform.h" #include "ui/ozone/platform/wayland/common/wayland_util.h" #include "ui/ozone/platform/wayland/host/wayland_subsurface.h" @@ -2028,7 +2029,28 @@ TEST_P(WaylandWindowTest, CreatesPopupOnTouchDownSerial) { auto* test_popup = GetPopupByWindow(popup.get()); ASSERT_TRUE(test_popup); - EXPECT_NE(test_popup->grab_serial(), touch_up_serial); + + // Touch events are the exception. We can't use the serial that was sent + // before the "up" event. Otherwise, some compositors may dismiss popups. + // Thus, no serial must be used. + EXPECT_EQ(test_popup->grab_serial(), 0U); + + popup->Hide(); + + // Send a single down event now. + wl_touch_send_down(server_.seat()->touch()->resource(), touch_down_serial, 0, + surface_->resource(), 0 /* id */, wl_fixed_from_int(50), + wl_fixed_from_int(100)); + + Sync(); + + popup->Show(false); + + Sync(); + + test_popup = GetPopupByWindow(popup.get()); + ASSERT_TRUE(test_popup); + EXPECT_EQ(test_popup->grab_serial(), touch_down_serial); } @@ -2107,8 +2129,9 @@ TEST_P(WaylandWindowTest, OneWaylandSubsurface) { auto* mock_surface_subsurface = server_.GetObject<wl::MockSurface>( wayland_subsurface->wayland_surface()->GetSurfaceId()); EXPECT_TRUE(mock_surface_subsurface); - wayland_subsurface->ConfigureAndShowSurface( - gfx::OVERLAY_TRANSFORM_NONE, subsurface_bounds, true, nullptr, nullptr); + wayland_subsurface->ConfigureAndShowSurface(gfx::OVERLAY_TRANSFORM_NONE, + gfx::RectF(), subsurface_bounds, + true, nullptr, nullptr); connection_->ScheduleFlush(); Sync(); @@ -2122,6 +2145,117 @@ TEST_P(WaylandWindowTest, OneWaylandSubsurface) { EXPECT_TRUE(test_subsurface->sync()); } +TEST_P(WaylandWindowTest, UsesCorrectParentForChildrenWindows) { + uint32_t serial = 0; + + MockPlatformWindowDelegate window_delegate; + std::unique_ptr<WaylandWindow> window = CreateWaylandWindowWithParams( + PlatformWindowType::kWindow, gfx::kNullAcceleratedWidget, + gfx::Rect(10, 10, 100, 100), &window_delegate); + EXPECT_TRUE(window); + + window->Show(false); + + std::unique_ptr<WaylandWindow> another_window = CreateWaylandWindowWithParams( + PlatformWindowType::kWindow, gfx::kNullAcceleratedWidget, + gfx::Rect(10, 10, 300, 400), &window_delegate); + EXPECT_TRUE(another_window); + + another_window->Show(false); + + Sync(); + + auto* window1 = window.get(); + auto* window2 = window_.get(); + auto* window3 = another_window.get(); + + // Make sure windows are not "active". + auto empty_state = MakeStateArray({}); + SendConfigureEvent(xdg_surface_, 0, 0, ++serial, empty_state.get()); + auto* xdg_surface_window = + server_ + .GetObject<wl::MockSurface>(window->root_surface()->GetSurfaceId()) + ->xdg_surface(); + SendConfigureEvent(xdg_surface_window, 0, 0, ++serial, empty_state.get()); + auto* xdg_surface_another_window = + server_ + .GetObject<wl::MockSurface>( + another_window->root_surface()->GetSurfaceId()) + ->xdg_surface(); + SendConfigureEvent(xdg_surface_another_window, 0, 0, ++serial, + empty_state.get()); + + Sync(); + + // Case 1: provided parent window's widget.. + MockPlatformWindowDelegate menu_window_delegate; + std::unique_ptr<WaylandWindow> menu_window = CreateWaylandWindowWithParams( + PlatformWindowType::kMenu, window1->GetWidget(), + gfx::Rect(10, 10, 10, 10), &menu_window_delegate); + + EXPECT_TRUE(menu_window->parent_window() == window1); + + // Case 2: didn't provide parent window's widget - must use current focused. + // + // Subcase 1: pointer focus. + window2->SetPointerFocus(true); + menu_window = CreateWaylandWindowWithParams( + PlatformWindowType::kMenu, gfx::kNullAcceleratedWidget, + gfx::Rect(10, 10, 10, 10), &menu_window_delegate); + + EXPECT_TRUE(menu_window->parent_window() == window2); + EXPECT_TRUE(wl::IsMenuType(menu_window->type())); + + // Subcase 2: keyboard focus. + window2->SetPointerFocus(false); + window2->set_keyboard_focus(true); + menu_window = CreateWaylandWindowWithParams( + PlatformWindowType::kMenu, gfx::kNullAcceleratedWidget, + gfx::Rect(10, 10, 10, 10), &menu_window_delegate); + + // Mustn't be able to create a menu window, but rather creates a toplevel + // window as we must provide at least something. + EXPECT_TRUE(menu_window); + // Make it create xdg objects. + menu_window->Show(false); + + Sync(); + + auto* menu_window_xdg = + server_ + .GetObject<wl::MockSurface>( + another_window->root_surface()->GetSurfaceId()) + ->xdg_surface(); + EXPECT_TRUE(menu_window_xdg); + EXPECT_TRUE(menu_window_xdg->xdg_toplevel()); + EXPECT_FALSE(menu_window_xdg->xdg_popup()); + + // Subcase 3: touch focus. + window2->set_keyboard_focus(false); + window2->set_touch_focus(true); + menu_window = CreateWaylandWindowWithParams( + PlatformWindowType::kMenu, gfx::kNullAcceleratedWidget, + gfx::Rect(10, 10, 10, 10), &menu_window_delegate); + + EXPECT_TRUE(menu_window->parent_window() == window2); + EXPECT_TRUE(wl::IsMenuType(menu_window->type())); + + // Case 3: neither of the windows are focused. However, there is one that is + // active. Must use that then. + window2->set_touch_focus(false); + + auto active = InitializeWlArrayWithActivatedState(); + SendConfigureEvent(xdg_surface_another_window, 0, 0, ++serial, active.get()); + Sync(); + + menu_window = CreateWaylandWindowWithParams( + PlatformWindowType::kMenu, gfx::kNullAcceleratedWidget, + gfx::Rect(10, 10, 10, 10), &menu_window_delegate); + + EXPECT_TRUE(menu_window->parent_window() == window3); + EXPECT_TRUE(wl::IsMenuType(menu_window->type())); +} + INSTANTIATE_TEST_SUITE_P(XdgVersionStableTest, WaylandWindowTest, ::testing::Values(kXdgShellStable)); diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_zwp_linux_dmabuf.cc b/chromium/ui/ozone/platform/wayland/host/wayland_zwp_linux_dmabuf.cc index 991ae63fc1c..d82674b3137 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_zwp_linux_dmabuf.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_zwp_linux_dmabuf.cc @@ -12,10 +12,6 @@ namespace ui { -namespace { -constexpr uint32_t kImmedVerstion = 3; -} - WaylandZwpLinuxDmabuf::WaylandZwpLinuxDmabuf( zwp_linux_dmabuf_v1* zwp_linux_dmabuf, WaylandConnection* connection) @@ -56,9 +52,9 @@ void WaylandZwpLinuxDmabuf::CreateBuffer(base::ScopedFD fd, } // It's possible to avoid waiting until the buffer is created and have it - // immediately. This method is only available since the protocol version 3. - if (zwp_linux_dmabuf_v1_get_version(zwp_linux_dmabuf_.get()) >= - kImmedVerstion) { + // immediately. This method is only available since the protocol version 2. + if (wl::get_version_of_object(zwp_linux_dmabuf_.get()) >= + ZWP_LINUX_BUFFER_PARAMS_V1_CREATE_IMMED_SINCE_VERSION) { wl::Object<wl_buffer> buffer(zwp_linux_buffer_params_v1_create_immed( params, size.width(), size.height(), format, 0)); std::move(callback).Run(std::move(buffer)); diff --git a/chromium/ui/ozone/platform/wayland/host/xdg_foreign_wrapper.cc b/chromium/ui/ozone/platform/wayland/host/xdg_foreign_wrapper.cc new file mode 100644 index 00000000000..511ad9e4ffb --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/host/xdg_foreign_wrapper.cc @@ -0,0 +1,128 @@ +// Copyright 2020 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/wayland/host/xdg_foreign_wrapper.h" + +#include <xdg-foreign-unstable-v1-client-protocol.h> + +#include "ui/ozone/platform/wayland/host/wayland_connection.h" +#include "ui/ozone/platform/wayland/host/wayland_window.h" +#include "ui/platform_window/platform_window_init_properties.h" + +namespace ui { + +struct XdgForeignWrapper::ExportedSurface { + ExportedSurface(wl_surface* surface, OnHandleExported cb); + ExportedSurface(ExportedSurface&& buffer); + ExportedSurface& operator=(ExportedSurface&& buffer); + ~ExportedSurface(); + + // Surface that is exported. + wl_surface* surface_for_export = nullptr; + + // Exported |surface|. + wl::Object<zxdg_exported_v1> exported; + + // Handle of the exported |surface|. + std::string exported_handle; + + // The cb that will be executed when |handle| is exported. + std::vector<OnHandleExported> callbacks; +}; + +XdgForeignWrapper::ExportedSurface::ExportedSurface(wl_surface* surface, + OnHandleExported cb) + : surface_for_export(surface) { + callbacks.emplace_back((std::move(cb))); +} + +XdgForeignWrapper::ExportedSurface::ExportedSurface(ExportedSurface&& buffer) = + default; + +XdgForeignWrapper::ExportedSurface& +XdgForeignWrapper::ExportedSurface::operator=(ExportedSurface&& buffer) = + default; + +XdgForeignWrapper::ExportedSurface::~ExportedSurface() = default; + +XdgForeignWrapper::XdgForeignWrapper(WaylandConnection* connection, + wl::Object<zxdg_exporter_v1> exporter_v1) + : connection_(connection), exporter_v1_(std::move(exporter_v1)) {} + +XdgForeignWrapper::~XdgForeignWrapper() = default; + +void XdgForeignWrapper::ExportSurfaceToForeign(WaylandWindow* window, + OnHandleExported cb) { + DCHECK_EQ(window->type(), PlatformWindowType::kWindow); + auto* surface = window->root_surface()->surface(); + auto* exported_surface = GetExportedSurface(surface); + if (!exported_surface) { + // The |surface| has never been exported. Export it and return the handle + // via the |cb|. + ExportSurfaceInternal(surface, std::move(cb)); + } else if (exported_surface->exported_handle.empty()) { + // The |surface| has already been exported, but its handle hasn't been + // received yet. Store the |cb| and execute when the handle is obtained. + exported_surface->callbacks.emplace_back(std::move(cb)); + } else { + // The |surface| has already been exported and its handle has been received. + // Execute the |cb| and send the handle. + DCHECK(!exported_surface->exported_handle.empty()); + std::move(cb).Run(exported_surface->exported_handle); + } +} + +XdgForeignWrapper::ExportedSurface* XdgForeignWrapper::GetExportedSurface( + wl_surface* surface) { + for (auto& item : exported_surfaces_) { + if (item.surface_for_export == surface) + return &item; + } + return nullptr; +} + +void XdgForeignWrapper::ExportSurfaceInternal(wl_surface* surface, + OnHandleExported cb) { + static const struct zxdg_exported_v1_listener kExportedListener = { + &XdgForeignWrapper::OnExported}; + + ExportedSurface exported_surface(surface, std::move(cb)); + exported_surface.exported.reset( + zxdg_exporter_v1_export(exporter_v1_.get(), surface)); + zxdg_exported_v1_add_listener(exported_surface.exported.get(), + &kExportedListener, this); + exported_surfaces_.emplace_back(std::move(exported_surface)); + connection_->ScheduleFlush(); +} + +void XdgForeignWrapper::OnWindowRemoved(WaylandWindow* window) { + auto it = std::find_if(exported_surfaces_.begin(), exported_surfaces_.end(), + [window](const auto& surface) { + return window->root_surface()->surface() == + surface.surface_for_export; + }); + if (it != exported_surfaces_.end()) + exported_surfaces_.erase(it); +} + +// static +void XdgForeignWrapper::OnExported(void* data, + zxdg_exported_v1* exported, + const char* handle) { + auto* self = static_cast<XdgForeignWrapper*>(data); + DCHECK(self); + + auto exported_surface_it = std::find_if( + self->exported_surfaces_.begin(), self->exported_surfaces_.end(), + [exported](const auto& item) { return item.exported.get() == exported; }); + DCHECK(exported_surface_it != self->exported_surfaces_.end()); + exported_surface_it->exported_handle = handle; + + for (auto& cb : exported_surface_it->callbacks) + std::move(cb).Run(exported_surface_it->exported_handle); + + exported_surface_it->callbacks.clear(); +} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/xdg_foreign_wrapper.h b/chromium/ui/ozone/platform/wayland/host/xdg_foreign_wrapper.h new file mode 100644 index 00000000000..50bb4f978db --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/host/xdg_foreign_wrapper.h @@ -0,0 +1,63 @@ +// Copyright 2020 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. + +#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_XDG_FOREIGN_WRAPPER_H_ +#define UI_OZONE_PLATFORM_WAYLAND_HOST_XDG_FOREIGN_WRAPPER_H_ + +#include <string> + +#include "base/callback.h" +#include "ui/ozone/platform/wayland/common/wayland_object.h" +#include "ui/ozone/platform/wayland/host/wayland_connection.h" +#include "ui/ozone/platform/wayland/host/wayland_window_observer.h" + +namespace ui { + +class WaylandConnection; + +// A wrapper for xdg foreign objects. Exports surfaces that have xdg_surface +// roles and asynchronously returns handles for them. Only xdg_surface surfaces +// may be exported. Currently supports only exporting surfaces. +// +// TODO(1126817): consider supporting xdg-foreign-v2. +class XdgForeignWrapper : public WaylandWindowObserver { + public: + using OnHandleExported = base::OnceCallback<void(const std::string&)>; + + XdgForeignWrapper(WaylandConnection* connection, + wl::Object<zxdg_exporter_v1> exporter_v1); + XdgForeignWrapper(const XdgForeignWrapper&) = delete; + XdgForeignWrapper& operator=(const XdgForeignWrapper&) = delete; + ~XdgForeignWrapper() override; + + // Exports |window|'s wl_surface and asynchronously returns a handle for that + // via the |cb|. Please note that wl_surface that has xdg_surface role can be + // exported. + void ExportSurfaceToForeign(WaylandWindow* window, OnHandleExported cb); + + private: + struct ExportedSurface; + + ExportedSurface* GetExportedSurface(wl_surface* surface); + + void ExportSurfaceInternal(wl_surface* surface, OnHandleExported cb); + + // WaylandWindowObserver: + void OnWindowRemoved(WaylandWindow* window) override; + + // zxdg_exported_v1_listener: + static void OnExported(void* data, + zxdg_exported_v1* exported, + const char* handle); + + WaylandConnection* const connection_; + + wl::Object<zxdg_exporter_v1> exporter_v1_; + + std::vector<ExportedSurface> exported_surfaces_; +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_WAYLAND_HOST_XDG_FOREIGN_WRAPPER_H_ diff --git a/chromium/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.cc b/chromium/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.cc index 99eb2e7012a..cee4e46e6c1 100644 --- a/chromium/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.cc +++ b/chromium/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.cc @@ -11,7 +11,9 @@ #include "base/environment.h" #include "base/nix/xdg_util.h" +#include "ui/events/event.h" #include "ui/events/event_constants.h" +#include "ui/events/types/event_type.h" #include "ui/gfx/geometry/rect.h" #include "ui/ozone/platform/wayland/common/wayland_util.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" @@ -295,10 +297,17 @@ bool XDGPopupWrapperImpl::Initialize(WaylandConnection* connection, if (!xdg_surface_ || !parent_xdg_surface) return false; + auto new_bounds = bounds; + // Wayland doesn't allow empty bounds. If a zero or negative size is set, the + // invalid_input error is raised. Thus, use the least possible one. + // WaylandPopup will update its bounds upon the following configure event. + if (new_bounds.IsEmpty()) + new_bounds.set_size({1, 1}); + if (connection->shell()) - return InitializeStable(connection, bounds, parent_xdg_surface); + return InitializeStable(connection, new_bounds, parent_xdg_surface); else if (connection->shell_v6()) - return InitializeV6(connection, bounds, parent_xdg_surface); + return InitializeV6(connection, new_bounds, parent_xdg_surface); return false; } @@ -456,34 +465,13 @@ bool XDGPopupWrapperImpl::CanGrabPopup(WaylandConnection* connection) const { if (connection->IsDragInProgress() || !connection->seat()) return false; - // According to the spec, the grab call can only be done on a key press, mouse - // press or touch down. However, there is a problem with popup windows and - // touch events so long as Chromium creates them only on consequent touch up - // events. That results in Wayland compositors dismissing popups. To fix the - // issue, do not use grab with touch events. Please note that explicit grab - // means that a Wayland compositor dismisses a popup whenever the user clicks - // outside the created surfaces. If the explicit grab is not used, the popups - // are not dismissed in such cases. What is more, current non-ozone X11 - // implementation does the same. This means there is no functionality changes - // and we do things right. - // - // We cannot know what was the last event. Instead, we can check if the window - // has pointer or keyboard focus. If so, the popup will be explicitly grabbed. - // - // There is a bug in the gnome/mutter - if explicit grab is not used, - // unmapping of a wl_surface (aka destroying xdg_popup and surface) to hide a - // window results in weird behaviour. That is, a popup continues to be visible - // on a display and it results in a crash of the entire session. Thus, just - // continue to use grab here and avoid showing popups for touch events on - // gnome/mutter. That is better than crashing the entire system. Otherwise, - // Chromium has to change the way how it reacts on touch events - instead of - // creating a menu on touch up, it must do it on touch down events. - // https://gitlab.gnome.org/GNOME/mutter/issues/698#note_562601. - std::unique_ptr<base::Environment> env(base::Environment::Create()); - return (base::nix::GetDesktopEnvironment(env.get()) == - base::nix::DESKTOP_ENVIRONMENT_GNOME) || - (wayland_window_->parent_window()->has_pointer_focus() || - wayland_window_->parent_window()->has_keyboard_focus()); + // According to the definition of the xdg protocol, the grab request must be + // used in response to some sort of user action like a button press, key + // press, or touch down event. + EventType last_event_type = connection->event_serial().event_type; + return last_event_type == ET_TOUCH_PRESSED || + last_event_type == ET_KEY_PRESSED || + last_event_type == ET_MOUSE_PRESSED; } void XDGPopupWrapperImpl::Configure(void* data, diff --git a/chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.cc b/chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.cc index f1a03831363..6ee18c65b7e 100644 --- a/chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.cc +++ b/chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.cc @@ -4,7 +4,7 @@ #include "ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.h" -#include <aura-shell-client-protocol.h> +#include <xdg-decoration-unstable-v1-client-protocol.h> #include <xdg-shell-client-protocol.h> #include <xdg-shell-unstable-v6-client-protocol.h> @@ -204,6 +204,16 @@ void XDGSurfaceWrapperImpl::CloseTopLevelStable( surface->wayland_window_->OnCloseRequest(); } +void XDGSurfaceWrapperImpl::SetTopLevelDecorationMode( + zxdg_toplevel_decoration_v1_mode requested_mode) { + if (requested_mode == decoration_mode_) + return; + + decoration_mode_ = requested_mode; + zxdg_toplevel_decoration_v1_set_mode(zxdg_toplevel_decoration_.get(), + requested_mode); +} + // static void XDGSurfaceWrapperImpl::ConfigureV6(void* data, struct zxdg_surface_v6* zxdg_surface_v6, @@ -255,6 +265,17 @@ xdg_surface* XDGSurfaceWrapperImpl::xdg_surface() const { return xdg_surface_.get(); } +// static +void XDGSurfaceWrapperImpl::ConfigureDecoration( + void* data, + struct zxdg_toplevel_decoration_v1* decoration, + uint32_t mode) { + auto* surface = static_cast<XDGSurfaceWrapperImpl*>(data); + DCHECK(surface); + surface->SetTopLevelDecorationMode( + static_cast<zxdg_toplevel_decoration_v1_mode>(mode)); +} + bool XDGSurfaceWrapperImpl::InitializeStable(bool with_toplevel) { static const xdg_surface_listener xdg_surface_listener = { &XDGSurfaceWrapperImpl::ConfigureStable, @@ -287,7 +308,11 @@ bool XDGSurfaceWrapperImpl::InitializeStable(bool with_toplevel) { LOG(ERROR) << "Failed to create xdg_toplevel"; return false; } + xdg_toplevel_add_listener(xdg_toplevel_.get(), &xdg_toplevel_listener, this); + + InitializeXdgDecoration(); + wayland_window_->root_surface()->Commit(); connection_->ScheduleFlush(); return true; @@ -329,16 +354,23 @@ bool XDGSurfaceWrapperImpl::InitializeV6(bool with_toplevel) { zxdg_toplevel_v6_add_listener(zxdg_toplevel_v6_.get(), &zxdg_toplevel_v6_listener, this); - if (connection_->aura_shell()) { - aura_surface_.reset(zaura_shell_get_aura_surface( - connection_->aura_shell(), wayland_window_->root_surface()->surface())); - zaura_surface_set_fullscreen_mode(aura_surface_.get(), - ZAURA_SURFACE_FULLSCREEN_MODE_IMMERSIVE); - } - wayland_window_->root_surface()->Commit(); connection_->ScheduleFlush(); return true; } +void XDGSurfaceWrapperImpl::InitializeXdgDecoration() { + if (connection_->xdg_decoration_manager_v1()) { + DCHECK(!zxdg_toplevel_decoration_); + static const zxdg_toplevel_decoration_v1_listener decoration_listener = { + &XDGSurfaceWrapperImpl::ConfigureDecoration, + }; + zxdg_toplevel_decoration_.reset( + zxdg_decoration_manager_v1_get_toplevel_decoration( + connection_->xdg_decoration_manager_v1(), xdg_toplevel_.get())); + zxdg_toplevel_decoration_v1_add_listener(zxdg_toplevel_decoration_.get(), + &decoration_listener, this); + } +} + } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.h b/chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.h index 2381aecfd9c..da945779a76 100644 --- a/chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.h +++ b/chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.h @@ -7,7 +7,11 @@ #include "ui/ozone/platform/wayland/host/shell_surface_wrapper.h" -#include "base/macros.h" +#include <xdg-decoration-unstable-v1-client-protocol.h> + +#include <cstdint> +#include <string> + #include "base/strings/string16.h" #include "ui/ozone/platform/wayland/common/wayland_object.h" @@ -25,6 +29,8 @@ class XDGSurfaceWrapperImpl : public ShellSurfaceWrapper { public: XDGSurfaceWrapperImpl(WaylandWindow* wayland_window, WaylandConnection* connection); + XDGSurfaceWrapperImpl(const XDGSurfaceWrapperImpl&) = delete; + XDGSurfaceWrapperImpl& operator=(const XDGSurfaceWrapperImpl&) = delete; ~XDGSurfaceWrapperImpl() override; // ShellSurfaceWrapper overrides: @@ -68,6 +74,14 @@ class XDGSurfaceWrapperImpl : public ShellSurfaceWrapper { static void CloseTopLevelV6(void* data, struct zxdg_toplevel_v6* zxdg_toplevel_v6); + void SetTopLevelDecorationMode( + zxdg_toplevel_decoration_v1_mode requested_mode); + // zxdg_decoration_listener + static void ConfigureDecoration( + void* data, + struct zxdg_toplevel_decoration_v1* decoration, + uint32_t mode); + struct xdg_surface* xdg_surface() const; zxdg_surface_v6* zxdg_surface() const; @@ -77,6 +91,9 @@ class XDGSurfaceWrapperImpl : public ShellSurfaceWrapper { // Initializes using XDG Shell V6 protocol. bool InitializeV6(bool with_toplevel); + // Initializes the xdg-decoration protocol extension, if available. + void InitializeXdgDecoration(); + // Non-owing WaylandWindow that uses this surface wrapper. WaylandWindow* const wayland_window_; WaylandConnection* const connection_; @@ -87,11 +104,14 @@ class XDGSurfaceWrapperImpl : public ShellSurfaceWrapper { wl::Object<zxdg_toplevel_v6> zxdg_toplevel_v6_; wl::Object<struct xdg_surface> xdg_surface_; wl::Object<xdg_toplevel> xdg_toplevel_; - wl::Object<zaura_surface> aura_surface_; + wl::Object<zxdg_toplevel_decoration_v1> zxdg_toplevel_decoration_; bool surface_for_popup_ = false; - DISALLOW_COPY_AND_ASSIGN(XDGSurfaceWrapperImpl); + // Keeps track of the decoration mode currently in use if xdg-decoration + // protocol extension is available, otherwise CLIENT_SIDE is assumed. + enum zxdg_toplevel_decoration_v1_mode decoration_mode_ = + ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE; }; } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/ozone_platform_wayland.cc b/chromium/ui/ozone/platform/wayland/ozone_platform_wayland.cc index 8de40eb6896..e4968f1bc42 100644 --- a/chromium/ui/ozone/platform/wayland/ozone_platform_wayland.cc +++ b/chromium/ui/ozone/platform/wayland/ozone_platform_wayland.cc @@ -12,6 +12,7 @@ #include "base/bind.h" #include "base/memory/ptr_util.h" #include "base/message_loop/message_pump_type.h" +#include "base/no_destructor.h" #include "base/threading/sequenced_task_runner_handle.h" #include "ui/base/buildflags.h" #include "ui/base/cursor/cursor_factory.h" @@ -63,23 +64,6 @@ namespace ui { namespace { -constexpr OzonePlatform::PlatformProperties kWaylandPlatformProperties = { - // Supporting server-side decorations requires a support of - // xdg-decorations. But this protocol has been accepted into the upstream - // recently, and it will take time before it is taken by compositors. For - // now, always use custom frames and disallow switching to server-side - // frames. - // https://github.com/wayland-project/wayland-protocols/commit/76d1ae8c65739eff3434ef219c58a913ad34e988 - .custom_frame_pref_default = true, - - // Wayland doesn't provide clients with global screen coordinates. Instead, - // it forces clients to position windows relative to their top level windows - // if the have child-parent relationship. In case of toplevel windows, - // clients simply don't know their position on screens and always assume - // they are located at some arbitrary position. - .ignore_screen_bounds_for_menus = true, -}; - constexpr OzonePlatform::InitializedHostProperties kWaylandInitializedHostProperties = { /*supports_overlays=*/false, @@ -232,7 +216,29 @@ class OzonePlatformWayland : public OzonePlatform { } const PlatformProperties& GetPlatformProperties() override { - return kWaylandPlatformProperties; + static base::NoDestructor<OzonePlatform::PlatformProperties> properties; + static bool initialised = false; + if (!initialised) { + // Supporting server-side decorations requires a support of + // xdg-decorations. But this protocol has been accepted into the upstream + // recently, and it will take time before it is taken by compositors. For + // now, always use custom frames and disallow switching to server-side + // frames. + // https://github.com/wayland-project/wayland-protocols/commit/76d1ae8c65739eff3434ef219c58a913ad34e988 + properties->custom_frame_pref_default = true; + + // Wayland doesn't provide clients with global screen coordinates. + // Instead, it forces clients to position windows relative to their top + // level windows if the have child-parent relationship. In case of + // toplevel windows, clients simply don't know their position on screens + // and always assume they are located at some arbitrary position. + properties->ignore_screen_bounds_for_menus = true; + properties->app_modal_dialogs_use_event_blocker = true; + + initialised = true; + } + + return *properties; } const InitializedHostProperties& GetInitializedHostProperties() override { @@ -252,6 +258,12 @@ class OzonePlatformWayland : public OzonePlatform { buffer_manager_->AddBindingWaylandBufferManagerGpu(std::move(receiver)); } + void PostMainMessageLoopStart( + base::OnceCallback<void()> shutdown_cb) override { + DCHECK(connection_); + connection_->SetShutdownCb(std::move(shutdown_cb)); + } + private: #if BUILDFLAG(USE_XKBCOMMON) XkbEvdevCodes xkb_evdev_code_converter_; diff --git a/chromium/ui/ozone/platform/wayland/test/mock_surface.cc b/chromium/ui/ozone/platform/wayland/test/mock_surface.cc index 524b4f45bc3..d7ec1ec227b 100644 --- a/chromium/ui/ozone/platform/wayland/test/mock_surface.cc +++ b/chromium/ui/ozone/platform/wayland/test/mock_surface.cc @@ -87,6 +87,10 @@ MockSurface::MockSurface(wl_resource* resource) : ServerObject(resource) {} MockSurface::~MockSurface() { if (xdg_surface_ && xdg_surface_->resource()) wl_resource_destroy(xdg_surface_->resource()); + if (sub_surface_ && sub_surface_->resource()) + wl_resource_destroy(sub_surface_->resource()); + if (viewport_ && viewport_->resource()) + wl_resource_destroy(viewport_->resource()); } MockSurface* MockSurface::FromResource(wl_resource* resource) { diff --git a/chromium/ui/ozone/platform/wayland/test/mock_surface.h b/chromium/ui/ozone/platform/wayland/test/mock_surface.h index e562a20b144..5c4e5cfb795 100644 --- a/chromium/ui/ozone/platform/wayland/test/mock_surface.h +++ b/chromium/ui/ozone/platform/wayland/test/mock_surface.h @@ -15,6 +15,7 @@ #include "ui/ozone/platform/wayland/test/mock_xdg_surface.h" #include "ui/ozone/platform/wayland/test/server_object.h" #include "ui/ozone/platform/wayland/test/test_subsurface.h" +#include "ui/ozone/platform/wayland/test/test_viewport.h" #include "ui/ozone/platform/wayland/test/test_xdg_popup.h" struct wl_resource; @@ -52,6 +53,9 @@ class MockSurface : public ServerObject { } TestSubSurface* sub_surface() const { return sub_surface_; } + void set_viewport(TestViewport* viewport) { viewport_ = viewport; } + TestViewport* viewport() { return viewport_; } + void set_frame_callback(wl_resource* callback_resource) { DCHECK(!frame_callback_); frame_callback_ = callback_resource; @@ -70,6 +74,7 @@ class MockSurface : public ServerObject { private: MockXdgSurface* xdg_surface_ = nullptr; TestSubSurface* sub_surface_ = nullptr; + TestViewport* viewport_ = nullptr; wl_resource* frame_callback_ = nullptr; diff --git a/chromium/ui/ozone/platform/wayland/test/test_viewport.cc b/chromium/ui/ozone/platform/wayland/test/test_viewport.cc new file mode 100644 index 00000000000..b82e86fd2fe --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/test/test_viewport.cc @@ -0,0 +1,49 @@ +// Copyright 2020 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/wayland/test/test_viewport.h" + +#include "base/notreached.h" +#include "ui/ozone/platform/wayland/test/mock_surface.h" + +namespace wl { + +namespace { + +void SetSource(wl_client* client, + wl_resource* resource, + wl_fixed_t x, + wl_fixed_t y, + wl_fixed_t width, + wl_fixed_t height) { + NOTIMPLEMENTED_LOG_ONCE(); +} + +void SetDestination(wl_client* client, + wl_resource* resource, + int32_t x, + int32_t y) { + NOTIMPLEMENTED_LOG_ONCE(); +} + +} // namespace + +const struct wp_viewport_interface kTestViewportImpl = { + DestroyResource, + SetSource, + SetDestination, +}; + +TestViewport::TestViewport(wl_resource* resource, wl_resource* surface) + : ServerObject(resource), surface_(surface) { + DCHECK(surface_); +} + +TestViewport::~TestViewport() { + auto* mock_surface = GetUserDataAs<MockSurface>(surface_); + if (mock_surface) + mock_surface->set_viewport(nullptr); +} + +} // namespace wl diff --git a/chromium/ui/ozone/platform/wayland/test/test_viewport.h b/chromium/ui/ozone/platform/wayland/test/test_viewport.h new file mode 100644 index 00000000000..0cea33c68e5 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/test/test_viewport.h @@ -0,0 +1,32 @@ +// Copyright 2020 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. + +#ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_VIEWPORT_H_ +#define UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_VIEWPORT_H_ + +#include <viewporter-server-protocol.h> + +#include "ui/ozone/platform/wayland/test/server_object.h" + +struct wl_resource; + +namespace wl { + +extern const struct wp_viewport_interface kTestViewportImpl; + +class TestViewport : public ServerObject { + public: + explicit TestViewport(wl_resource* resource, wl_resource* surface); + ~TestViewport() override; + TestViewport(const TestViewport& rhs) = delete; + TestViewport& operator=(const TestViewport& rhs) = delete; + + private: + // Surface resource that is the ground for this Viewport. + wl_resource* surface_ = nullptr; +}; + +} // namespace wl + +#endif // UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_VIEWPORT_H_ diff --git a/chromium/ui/ozone/platform/wayland/test/test_viewporter.cc b/chromium/ui/ozone/platform/wayland/test/test_viewporter.cc new file mode 100644 index 00000000000..1b44f81105e --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/test/test_viewporter.cc @@ -0,0 +1,53 @@ +// Copyright 2020 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/wayland/test/test_viewporter.h" + +#include <viewporter-server-protocol.h> +#include <wayland-server-core.h> + +#include "base/check.h" +#include "ui/ozone/platform/wayland/test/mock_surface.h" +#include "ui/ozone/platform/wayland/test/test_viewport.h" + +namespace wl { + +namespace { + +constexpr uint32_t kViewporterVersion = 1; + +void GetViewport(struct wl_client* client, + struct wl_resource* resource, + uint32_t id, + struct wl_resource* surface) { + auto* mock_surface = GetUserDataAs<MockSurface>(surface); + if (mock_surface->viewport()) { + wl_resource_post_error(resource, WP_VIEWPORTER_ERROR_VIEWPORT_EXISTS, + "viewport exists"); + return; + } + + wl_resource* viewport_resource = + CreateResourceWithImpl<::testing::NiceMock<TestViewport>>( + client, &wp_viewport_interface, wl_resource_get_version(resource), + &kTestViewportImpl, id, surface); + DCHECK(viewport_resource); + mock_surface->set_viewport(GetUserDataAs<TestViewport>(viewport_resource)); +} + +} // namespace + +const struct wp_viewporter_interface kTestViewporterImpl = { + DestroyResource, + GetViewport, +}; + +TestViewporter::TestViewporter() + : GlobalObject(&wp_viewporter_interface, + &kTestViewporterImpl, + kViewporterVersion) {} + +TestViewporter::~TestViewporter() = default; + +} // namespace wl diff --git a/chromium/ui/ozone/platform/wayland/test/test_viewporter.h b/chromium/ui/ozone/platform/wayland/test/test_viewporter.h new file mode 100644 index 00000000000..6f8281a5686 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/test/test_viewporter.h @@ -0,0 +1,23 @@ +// Copyright 2020 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. + +#ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_VIEWPORTER_H_ +#define UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_VIEWPORTER_H_ + +#include "ui/ozone/platform/wayland/test/global_object.h" + +namespace wl { + +// Manage wl_viewporter object. +class TestViewporter : public GlobalObject { + public: + TestViewporter(); + ~TestViewporter() override; + TestViewporter(const TestViewporter& rhs) = delete; + TestViewporter& operator=(const TestViewporter& rhs) = delete; +}; + +} // namespace wl + +#endif // UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_VIEWPORTER_H_ diff --git a/chromium/ui/ozone/platform/wayland/test/test_wayland_server_thread.cc b/chromium/ui/ozone/platform/wayland/test/test_wayland_server_thread.cc index c6f0fe7cabc..6cf79816b5c 100644 --- a/chromium/ui/ozone/platform/wayland/test/test_wayland_server_thread.cc +++ b/chromium/ui/ozone/platform/wayland/test/test_wayland_server_thread.cc @@ -58,6 +58,8 @@ bool TestWaylandServerThread::Start(uint32_t shell_version) { return false; if (!sub_compositor_.Initialize(display_.get())) return false; + if (!viewporter_.Initialize(display_.get())) + return false; if (!output_.Initialize(display_.get())) return false; SetupOutputs(); diff --git a/chromium/ui/ozone/platform/wayland/test/test_wayland_server_thread.h b/chromium/ui/ozone/platform/wayland/test/test_wayland_server_thread.h index e3d4d489202..712ce602bf1 100644 --- a/chromium/ui/ozone/platform/wayland/test/test_wayland_server_thread.h +++ b/chromium/ui/ozone/platform/wayland/test/test_wayland_server_thread.h @@ -22,6 +22,7 @@ #include "ui/ozone/platform/wayland/test/test_output.h" #include "ui/ozone/platform/wayland/test/test_seat.h" #include "ui/ozone/platform/wayland/test/test_subcompositor.h" +#include "ui/ozone/platform/wayland/test/test_viewporter.h" #include "ui/ozone/platform/wayland/test/test_zwp_text_input_manager.h" struct wl_client; @@ -111,6 +112,7 @@ class TestWaylandServerThread : public base::Thread, // Represent Wayland global objects TestCompositor compositor_; TestSubCompositor sub_compositor_; + TestViewporter viewporter_; TestDataDeviceManager data_device_manager_; TestOutput output_; TestSeat seat_; diff --git a/chromium/ui/ozone/platform/wayland/test/wayland_test.cc b/chromium/ui/ozone/platform/wayland/test/wayland_test.cc index 4ce3ad031cd..b155af688f8 100644 --- a/chromium/ui/ozone/platform/wayland/test/wayland_test.cc +++ b/chromium/ui/ozone/platform/wayland/test/wayland_test.cc @@ -4,7 +4,10 @@ #include "ui/ozone/platform/wayland/test/wayland_test.h" +#include <memory> + #include "base/run_loop.h" +#include "ui/base/ui_base_features.h" #include "ui/events/ozone/layout/keyboard_layout_engine_manager.h" #include "ui/events/ozone/layout/scoped_keyboard_layout_engine.h" #include "ui/ozone/platform/wayland/host/wayland_output_manager.h" @@ -43,6 +46,15 @@ WaylandTest::WaylandTest() WaylandTest::~WaylandTest() {} void WaylandTest::SetUp() { + // TODO(1096425): remove this once Ozone is default on Linux. This is required + // to be able to run ozone_unittests locally without passing + // --enable-features=UseOzonePlatform explicitly. linux-ozone-rel bot does + // that automatically through changes done to "variants", which is also + // convenient to have locally so that we don't need to worry about that (it's + // the Wayland DragAndDrop that relies on the feature). + feature_list_.InitAndEnableFeature(features::kUseOzonePlatform); + ASSERT_TRUE(features::IsUsingOzonePlatform()); + ASSERT_TRUE(server_.Start(GetParam())); ASSERT_TRUE(connection_->Initialize()); screen_ = connection_->wayland_output_manager()->CreateWaylandScreen( diff --git a/chromium/ui/ozone/platform/wayland/test/wayland_test.h b/chromium/ui/ozone/platform/wayland/test/wayland_test.h index 97df3efbade..f08e25a4bc3 100644 --- a/chromium/ui/ozone/platform/wayland/test/wayland_test.h +++ b/chromium/ui/ozone/platform/wayland/test/wayland_test.h @@ -7,6 +7,7 @@ #include <memory> +#include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/buildflags.h" @@ -82,6 +83,7 @@ class WaylandTest : public ::testing::TestWithParam<uint32_t> { #endif std::unique_ptr<KeyboardLayoutEngine> keyboard_layout_engine_; + base::test::ScopedFeatureList feature_list_; DISALLOW_COPY_AND_ASSIGN(WaylandTest); }; diff --git a/chromium/ui/ozone/platform/x11/ozone_platform_x11.cc b/chromium/ui/ozone/platform/x11/ozone_platform_x11.cc index 4997e86af1a..3d9e737c0c9 100644 --- a/chromium/ui/ozone/platform/x11/ozone_platform_x11.cc +++ b/chromium/ui/ozone/platform/x11/ozone_platform_x11.cc @@ -9,12 +9,14 @@ #include "base/message_loop/message_pump_type.h" #include "base/metrics/histogram_functions.h" +#include "base/no_destructor.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" #include "ui/base/buildflags.h" #include "ui/base/cursor/cursor_factory.h" #include "ui/base/dragdrop/os_exchange_data_provider_factory.h" #include "ui/base/dragdrop/os_exchange_data_provider_factory_ozone.h" +#include "ui/base/ime/linux/linux_input_method_context_factory.h" #include "ui/base/x/x11_cursor_factory.h" #include "ui/base/x/x11_error_handler.h" #include "ui/base/x/x11_util.h" @@ -43,8 +45,7 @@ #include "ui/base/dragdrop/os_exchange_data_provider_non_backed.h" #include "ui/base/ime/chromeos/input_method_chromeos.h" #else -#include "ui/base/ime/linux/input_method_auralinux.h" // nogncheck -#include "ui/base/ime/linux/linux_input_method_context_factory.h" // nogncheck +#include "ui/base/ime/linux/input_method_auralinux.h" #include "ui/ozone/platform/x11/x11_os_exchange_data_provider_ozone.h" #endif @@ -64,26 +65,13 @@ namespace ui { namespace { -constexpr OzonePlatform::PlatformProperties kX11PlatformProperties{ - .needs_view_token = false, - .custom_frame_pref_default = false, - .use_system_title_bar = true, - - // When the Ozone X11 backend is running, use a UI loop to grab Expose - // events. See GLSurfaceGLX and https://crbug.com/326995. - .message_pump_type_for_gpu = base::MessagePumpType::UI, - // When the Ozone X11 backend is running, use a UI loop to dispatch - // SHM completion events. - .message_pump_type_for_viz_compositor = base::MessagePumpType::UI, - .supports_vulkan_swap_chain = true, - .platform_shows_drag_image = false, - .supports_global_application_menus = true}; - // Singleton OzonePlatform implementation for X11 platform. class OzonePlatformX11 : public OzonePlatform, public ui::OSExchangeDataProviderFactoryOzone { public: - OzonePlatformX11() { SetInstance(this); } + OzonePlatformX11() { + SetInstance(this); + } ~OzonePlatformX11() override {} @@ -165,7 +153,28 @@ class OzonePlatformX11 : public OzonePlatform, } const PlatformProperties& GetPlatformProperties() override { - return kX11PlatformProperties; + static base::NoDestructor<OzonePlatform::PlatformProperties> properties; + static bool initialised = false; + if (!initialised) { + properties->custom_frame_pref_default = ui::GetCustomFramePrefDefault(); + properties->use_system_title_bar = true; + + // When the Ozone X11 backend is running, use a UI loop to grab Expose + // events. See GLSurfaceGLX and https://crbug.com/326995. + properties->message_pump_type_for_gpu = base::MessagePumpType::UI; + // When the Ozone X11 backend is running, use a UI loop to dispatch + // SHM completion events. + properties->message_pump_type_for_viz_compositor = + base::MessagePumpType::UI; + properties->supports_vulkan_swap_chain = true; + properties->platform_shows_drag_image = false; + properties->supports_global_application_menus = true; + properties->app_modal_dialogs_use_event_blocker = true; + + initialised = true; + } + + return *properties; } void InitializeUI(const InitParams& params) override { diff --git a/chromium/ui/ozone/platform/x11/x11_canvas_surface.cc b/chromium/ui/ozone/platform/x11/x11_canvas_surface.cc index ce6c1d8fd1e..7ece6c9954d 100644 --- a/chromium/ui/ozone/platform/x11/x11_canvas_surface.cc +++ b/chromium/ui/ozone/platform/x11/x11_canvas_surface.cc @@ -8,11 +8,12 @@ #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkImageInfo.h" #include "ui/gfx/vsync_provider.h" +#include "ui/gfx/x/connection.h" namespace ui { X11CanvasSurface::X11CanvasSurface(gfx::AcceleratedWidget widget) - : x11_software_bitmap_presenter_(widget) {} + : x11_software_bitmap_presenter_(x11::Connection::Get(), widget, true) {} X11CanvasSurface::~X11CanvasSurface() = default; diff --git a/chromium/ui/ozone/platform/x11/x11_clipboard_ozone.cc b/chromium/ui/ozone/platform/x11/x11_clipboard_ozone.cc index 16a04768639..9cc7e1bc245 100644 --- a/chromium/ui/ozone/platform/x11/x11_clipboard_ozone.cc +++ b/chromium/ui/ozone/platform/x11/x11_clipboard_ozone.cc @@ -16,6 +16,7 @@ #include "ui/gfx/x/x11_atom_cache.h" #include "ui/gfx/x/x11_types.h" #include "ui/gfx/x/xproto.h" +#include "ui/gfx/x/xproto_util.h" using base::Contains; @@ -183,8 +184,8 @@ bool X11ClipboardOzone::OnSelectionRequest( .target = event.target, .property = event.property, }; - SendEvent(selection_event, selection_event.requestor, - x11::EventMask::NoEvent); + x11::SendEvent(selection_event, selection_event.requestor, + x11::EventMask::NoEvent); return true; } diff --git a/chromium/ui/ozone/platform/x11/x11_screen_ozone.cc b/chromium/ui/ozone/platform/x11/x11_screen_ozone.cc index 76aeb9e1c14..b98ae4ed908 100644 --- a/chromium/ui/ozone/platform/x11/x11_screen_ozone.cc +++ b/chromium/ui/ozone/platform/x11/x11_screen_ozone.cc @@ -10,6 +10,8 @@ #include "ui/events/platform/x11/x11_event_source.h" #include "ui/gfx/font_render_params.h" #include "ui/gfx/geometry/dip_util.h" +#include "ui/gfx/geometry/point_conversions.h" +#include "ui/gfx/geometry/rect_conversions.h" #include "ui/gfx/native_widget_types.h" #include "ui/platform_window/x11/x11_topmost_window_finder.h" #include "ui/platform_window/x11/x11_window.h" @@ -29,10 +31,6 @@ float GetDeviceScaleFactor() { return device_scale_factor; } -gfx::Point PixelToDIPPoint(const gfx::Point& pixel_point) { - return gfx::ConvertPointToDIP(GetDeviceScaleFactor(), pixel_point); -} - } // namespace X11ScreenOzone::X11ScreenOzone() @@ -74,14 +72,20 @@ display::Display X11ScreenOzone::GetDisplayForAcceleratedWidget( } gfx::Point X11ScreenOzone::GetCursorScreenPoint() const { + base::Optional<gfx::Point> point_in_pixels; if (ui::X11EventSource::HasInstance()) { - base::Optional<gfx::Point> point = - ui::X11EventSource::GetInstance() - ->GetRootCursorLocationFromCurrentEvent(); - if (point) - return PixelToDIPPoint(point.value()); + point_in_pixels = ui::X11EventSource::GetInstance() + ->GetRootCursorLocationFromCurrentEvent(); + } + if (!point_in_pixels) { + // This call is expensive so we explicitly only call it when + // |point_in_pixels| is not set. We note that base::Optional::value_or() + // would cause it to be called regardless. + point_in_pixels = GetCursorLocation(); } - return PixelToDIPPoint(GetCursorLocation()); + // TODO(danakj): Should this be rounded? Or kept as a floating point? + return gfx::ToFlooredPoint( + gfx::ConvertPointToDips(*point_in_pixels, GetDeviceScaleFactor())); } gfx::AcceleratedWidget X11ScreenOzone::GetAcceleratedWidgetAtScreenPoint( @@ -107,14 +111,19 @@ display::Display X11ScreenOzone::GetDisplayNearestPoint( } display::Display X11ScreenOzone::GetDisplayMatching( - const gfx::Rect& match_rect) const { + const gfx::Rect& match_rect_in_pixels) const { + gfx::Rect match_rect = gfx::ToEnclosingRect( + gfx::ConvertRectToDips(match_rect_in_pixels, GetDeviceScaleFactor())); const display::Display* matching_display = display::FindDisplayWithBiggestIntersection( - x11_display_manager_->displays(), - gfx::ConvertRectToDIP(GetDeviceScaleFactor(), match_rect)); + x11_display_manager_->displays(), match_rect); return matching_display ? *matching_display : GetPrimaryDisplay(); } +void X11ScreenOzone::SetScreenSaverSuspended(bool suspend) { + SuspendX11ScreenSaver(suspend); +} + void X11ScreenOzone::AddObserver(display::DisplayObserver* observer) { x11_display_manager_->AddObserver(observer); } diff --git a/chromium/ui/ozone/platform/x11/x11_screen_ozone.h b/chromium/ui/ozone/platform/x11/x11_screen_ozone.h index de7d392d17b..6aba14834f3 100644 --- a/chromium/ui/ozone/platform/x11/x11_screen_ozone.h +++ b/chromium/ui/ozone/platform/x11/x11_screen_ozone.h @@ -46,7 +46,8 @@ class X11ScreenOzone : public PlatformScreen, display::Display GetDisplayNearestPoint( const gfx::Point& point) const override; display::Display GetDisplayMatching( - const gfx::Rect& match_rect) const override; + const gfx::Rect& match_rect_in_pixels) const override; + void SetScreenSaverSuspended(bool suspend) override; void AddObserver(display::DisplayObserver* observer) override; void RemoveObserver(display::DisplayObserver* observer) override; std::string GetCurrentWorkspace() override; diff --git a/chromium/ui/ozone/platform_selection.cc b/chromium/ui/ozone/platform_selection.cc index 1b49a08af70..eb8e85edba6 100644 --- a/chromium/ui/ozone/platform_selection.cc +++ b/chromium/ui/ozone/platform_selection.cc @@ -33,6 +33,9 @@ int GetOzonePlatformId() { return g_selected_platform; std::string platform_name = GetPlatformName(); + // TODO(b/169115289) remove once all Tast tests use "drm". + if (platform_name == "gbm") + platform_name = "drm"; // Search for a matching platform in the list. for (int platform_id = 0; platform_id < kPlatformCount; ++platform_id) { diff --git a/chromium/ui/ozone/public/input_controller.cc b/chromium/ui/ozone/public/input_controller.cc index 43a27b43952..43e5fca8d2e 100644 --- a/chromium/ui/ozone/public/input_controller.cc +++ b/chromium/ui/ozone/public/input_controller.cc @@ -21,6 +21,7 @@ class StubInputController : public InputController { // InputController: bool HasMouse() override { return false; } + bool HasPointingStick() override { return false; } bool HasTouchpad() override { return false; } bool IsCapsLockEnabled() override { return false; } void SetCapsLockEnabled(bool enabled) override {} diff --git a/chromium/ui/ozone/public/input_controller.h b/chromium/ui/ozone/public/input_controller.h index cbf94825a32..27b5ad3acd8 100644 --- a/chromium/ui/ozone/public/input_controller.h +++ b/chromium/ui/ozone/public/input_controller.h @@ -43,6 +43,7 @@ class COMPONENT_EXPORT(OZONE_BASE) InputController { // Functions for checking devices existence. virtual bool HasMouse() = 0; + virtual bool HasPointingStick() = 0; virtual bool HasTouchpad() = 0; // Keyboard settings. diff --git a/chromium/ui/ozone/public/mojom/drm_device.mojom b/chromium/ui/ozone/public/mojom/drm_device.mojom index 6504ea5dddd..caf4a7e5a7b 100644 --- a/chromium/ui/ozone/public/mojom/drm_device.mojom +++ b/chromium/ui/ozone/public/mojom/drm_device.mojom @@ -57,11 +57,16 @@ interface DrmDevice { array<display.mojom.DisplayConfigurationParams> config_requests) => (map<int64, bool> statuses); - // Gets or sets high-definition content protection (HDCP) (DRM as in + // Gets high-definition content protection (HDCP) (DRM as in // digital rights management) state. GetHDCPState(int64 display_id) => - (int64 display_id, bool success, display.mojom.HDCPState state); - SetHDCPState(int64 display_id, display.mojom.HDCPState state) => + (int64 display_id, bool success, display.mojom.HDCPState state, + display.mojom.ContentProtectionMethod protection_method); + + // Sets high-definition content protection (HDCP) (DRM as in + // digital rights management) state. + SetHDCPState(int64 display_id, display.mojom.HDCPState state, + display.mojom.ContentProtectionMethod protection_method) => (int64 display_id, bool success); // Sets a 3x3 color transform matrix on the display hardware. diff --git a/chromium/ui/ozone/public/ozone_platform.cc b/chromium/ui/ozone/public/ozone_platform.cc index c7027b208f0..bcc577c5494 100644 --- a/chromium/ui/ozone/public/ozone_platform.cc +++ b/chromium/ui/ozone/public/ozone_platform.cc @@ -36,6 +36,9 @@ void EnsureInstance() { } // namespace +OzonePlatform::PlatformProperties::PlatformProperties() = default; +OzonePlatform::PlatformProperties::~PlatformProperties() = default; + OzonePlatform::OzonePlatform() { DCHECK(!g_instance) << "There should only be a single OzonePlatform."; g_instance = this; diff --git a/chromium/ui/ozone/public/ozone_platform.h b/chromium/ui/ozone/public/ozone_platform.h index 797c79d057e..764945ed097 100644 --- a/chromium/ui/ozone/public/ozone_platform.h +++ b/chromium/ui/ozone/public/ozone_platform.h @@ -70,6 +70,11 @@ class COMPONENT_EXPORT(OZONE) OzonePlatform { // Struct used to indicate platform properties. struct PlatformProperties { + PlatformProperties(); + PlatformProperties(const PlatformProperties& other) = delete; + PlatformProperties& operator=(const PlatformProperties& other) = delete; + ~PlatformProperties(); + // Fuchsia only: set to true when the platforms requires |view_token| field // in PlatformWindowInitProperties when creating a window. bool needs_view_token = false; @@ -105,6 +110,10 @@ class COMPONENT_EXPORT(OZONE) OzonePlatform { // Linux only, but see a TODO in BrowserDesktopWindowTreeHostLinux. // Determines whether the platform supports the global application menu. bool supports_global_application_menus = false; + + // Determines if the application modal dialogs should use the event blocker + // to allow the only browser window receiving UI events. + bool app_modal_dialogs_use_event_blocker = false; }; // Properties available in the host process after initialization. diff --git a/chromium/ui/ozone/public/platform_screen.cc b/chromium/ui/ozone/public/platform_screen.cc index c2440a5b237..8b58cf95caa 100644 --- a/chromium/ui/ozone/public/platform_screen.cc +++ b/chromium/ui/ozone/public/platform_screen.cc @@ -23,4 +23,8 @@ std::string PlatformScreen::GetCurrentWorkspace() { return {}; } +void PlatformScreen::SetScreenSaverSuspended(bool suspend) { + NOTIMPLEMENTED_LOG_ONCE(); +} + } // namespace ui diff --git a/chromium/ui/ozone/public/platform_screen.h b/chromium/ui/ozone/public/platform_screen.h index ba20597d8c5..71cc12c195b 100644 --- a/chromium/ui/ozone/public/platform_screen.h +++ b/chromium/ui/ozone/public/platform_screen.h @@ -77,6 +77,9 @@ class COMPONENT_EXPORT(OZONE_BASE) PlatformScreen { virtual display::Display GetDisplayMatching( const gfx::Rect& match_rect) const = 0; + // Suspends the platform-specific screensaver, if applicable. + virtual void SetScreenSaverSuspended(bool suspend); + // Adds/Removes display observers. virtual void AddObserver(display::DisplayObserver* observer) = 0; virtual void RemoveObserver(display::DisplayObserver* observer) = 0; |