diff options
-rwxr-xr-x | .ci/.gitlab-ci-checkcommit.sh | 2 | ||||
-rw-r--r-- | .ci/Makefile | 5 | ||||
-rw-r--r-- | .gitlab-ci.yml | 4 | ||||
-rw-r--r-- | Android.bp | 1 | ||||
-rw-r--r-- | README.md | 2 | ||||
-rwxr-xr-x | build_deploy.sh | 26 | ||||
-rw-r--r-- | compositor/DrmDisplayCompositor.cpp | 13 | ||||
-rw-r--r-- | compositor/DrmDisplayCompositor.h | 2 | ||||
-rw-r--r-- | drm/DrmConnector.cpp | 6 | ||||
-rw-r--r-- | drm/DrmDevice.cpp | 66 | ||||
-rw-r--r-- | drm/DrmDevice.h | 3 | ||||
-rw-r--r-- | drm/DrmFbImporter.cpp | 2 | ||||
-rw-r--r-- | drm/DrmMode.cpp | 3 | ||||
-rw-r--r-- | drm/ResourceManager.h | 6 | ||||
-rw-r--r-- | drm/UEventListener.cpp | 4 | ||||
-rw-r--r-- | drm/VSyncWorker.cpp | 2 | ||||
-rw-r--r-- | hwc2_device/DrmHwcTwo.cpp | 56 | ||||
-rw-r--r-- | hwc2_device/DrmHwcTwo.h | 6 | ||||
-rw-r--r-- | hwc2_device/HwcDisplay.cpp | 57 | ||||
-rw-r--r-- | hwc2_device/HwcDisplay.h | 10 | ||||
-rw-r--r-- | hwc2_device/HwcDisplayConfigs.cpp | 49 | ||||
-rw-r--r-- | hwc2_device/HwcDisplayConfigs.h | 5 | ||||
-rw-r--r-- | hwc2_device/hwc2_device.cpp | 35 | ||||
-rw-r--r-- | utils/autolock.cpp | 57 | ||||
-rw-r--r-- | utils/autolock.h | 42 |
25 files changed, 223 insertions, 241 deletions
diff --git a/.ci/.gitlab-ci-checkcommit.sh b/.ci/.gitlab-ci-checkcommit.sh index e59ad9f..c9c2e49 100755 --- a/.ci/.gitlab-ci-checkcommit.sh +++ b/.ci/.gitlab-ci-checkcommit.sh @@ -50,7 +50,7 @@ git log --pretty='%h' FETCH_HEAD..HEAD | while read h; do exit 1 fi - git show "$h" -- | clang-format-diff-12 -p 1 -style=file > /tmp/format-fixup.patch + git show "$h" -- | clang-format-diff-13 -p 1 -style=file > /tmp/format-fixup.patch if [ -s /tmp/format-fixup.patch ]; then cat /tmp/format-fixup.patch >&2 exit 1 diff --git a/.ci/Makefile b/.ci/Makefile index 4ca14d6..7f43a8a 100644 --- a/.ci/Makefile +++ b/.ci/Makefile @@ -2,8 +2,8 @@ INCLUDE_DIRS := . ../libdrm/include/drm include ./.ci/android_headers ./tests/test_include SYSTEM_INCLUDE_DIRS := /usr/include/libdrm -CLANG := clang++-12 -CLANG_TIDY := clang-tidy-12 +CLANG := clang++-13 +CLANG_TIDY := clang-tidy-13 OUT_DIR := /tmp/drm_hwcomposer/build SRC_DIR := . @@ -57,6 +57,7 @@ TIDY_CHECKS_FINE := * \ TIDY_CHECKS_NORMAL := \ $(TIDY_CHECKS_FINE) \ -hicpp* \ + -bugprone-easily-swappable-parameters \ -cppcoreguidelines-special-member-functions \ -cppcoreguidelines-avoid-c-arrays \ -cppcoreguidelines-pro-bounds-array-to-pointer-decay \ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9a54b44..89ac1b8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,11 +1,11 @@ -image: ubuntu:21.04 +image: ubuntu:21.10 variables: DEBIAN_FRONTEND: noninteractive before_script: - apt-get --quiet update --yes >/dev/null - - apt-get --quiet install --yes clang-12 clang-tidy-12 clang-format-12 git libdrm-dev blueprint-tools libgtest-dev make >/dev/null + - apt-get --quiet install --yes clang-13 clang-tidy-13 clang-format-13 git libdrm-dev blueprint-tools libgtest-dev make >/dev/null stages: - build @@ -117,7 +117,6 @@ filegroup { "drm/UEventListener.cpp", "drm/VSyncWorker.cpp", - "utils/autolock.cpp", "utils/hwcutils.cpp", "backend/Backend.cpp", @@ -16,7 +16,7 @@ A short list of contribution guidelines: you with formatting of your patches: ``` - git diff | clang-format-diff-12 -p 1 -style=file + git diff | clang-format-diff-13 -p 1 -style=file ``` * Hardware specific changes should be tested on relevant platforms before diff --git a/build_deploy.sh b/build_deploy.sh new file mode 100755 index 0000000..ba9732b --- /dev/null +++ b/build_deploy.sh @@ -0,0 +1,26 @@ +#!/bin/bash -e + +# To see logs after deploy run: $ HWCLOG=1 TESTDEV=<DEV> ./build_deploy.sh + +[ -z "$TESTDEV" ] && echo "Run $ TESTDEV=<Your lunch target> ./build_deploy.sh" && false + +cd ../.. +. build/envsetup.sh +lunch $TESTDEV +cd - + +mm + +adb root && adb remount && adb sync vendor + +adb shell stop +adb shell stop vendor.hwcomposer-2-1 || true +adb shell stop vendor.hwcomposer-2-2 || true +adb shell stop vendor.hwcomposer-2-3 || true +adb shell stop vendor.hwcomposer-2-4 || true + +[ $HWCLOG -eq "1" ] && adb logcat -c + +adb shell start + +[ $HWCLOG -eq "1" ] && adb logcat | grep -i hwc diff --git a/compositor/DrmDisplayCompositor.cpp b/compositor/DrmDisplayCompositor.cpp index 89e7f2d..c2e51ee 100644 --- a/compositor/DrmDisplayCompositor.cpp +++ b/compositor/DrmDisplayCompositor.cpp @@ -35,7 +35,6 @@ #include "drm/DrmDevice.h" #include "drm/DrmPlane.h" #include "drm/DrmUnique.h" -#include "utils/autolock.h" #include "utils/log.h" namespace android { @@ -54,18 +53,6 @@ auto DrmDisplayCompositor::Init(ResourceManager *resource_manager, int display) return 0; } -std::unique_ptr<DrmDisplayComposition> -DrmDisplayCompositor::CreateInitializedComposition() const { - DrmDevice *drm = resource_manager_->GetDrmDevice(display_); - DrmCrtc *crtc = drm->GetCrtcForDisplay(display_); - if (!crtc) { - ALOGE("Failed to find crtc for display = %d", display_); - return std::unique_ptr<DrmDisplayComposition>(); - } - - return std::make_unique<DrmDisplayComposition>(crtc); -} - // NOLINTNEXTLINE (readability-function-cognitive-complexity): Fixme auto DrmDisplayCompositor::CommitFrame(AtomicCommitArgs &args) -> int { ATRACE_CALL(); diff --git a/compositor/DrmDisplayCompositor.h b/compositor/DrmDisplayCompositor.h index e7899ed..9679520 100644 --- a/compositor/DrmDisplayCompositor.h +++ b/compositor/DrmDisplayCompositor.h @@ -58,8 +58,6 @@ class DrmDisplayCompositor { ~DrmDisplayCompositor() = default; auto Init(ResourceManager *resource_manager, int display) -> int; - std::unique_ptr<DrmDisplayComposition> CreateInitializedComposition() const; - auto ExecuteAtomicCommit(AtomicCommitArgs &args) -> int; DrmDisplayCompositor(const DrmDisplayCompositor &) = delete; diff --git a/drm/DrmConnector.cpp b/drm/DrmConnector.cpp index 32b6d24..7cbec95 100644 --- a/drm/DrmConnector.cpp +++ b/drm/DrmConnector.cpp @@ -102,12 +102,12 @@ auto DrmConnector::GetEdidBlob() -> DrmModePropertyBlobUnique { uint64_t blob_id = 0; int ret = UpdateEdidProperty(); if (ret != 0) { - return DrmModePropertyBlobUnique(); + return {}; } std::tie(ret, blob_id) = edid_property().value(); if (ret != 0) { - return DrmModePropertyBlobUnique(); + return {}; } return MakeDrmModePropertyBlobUnique(drm_->fd(), blob_id); @@ -175,6 +175,8 @@ int DrmConnector::UpdateModes() { return -ENODEV; } + state_ = c->connection; + modes_.clear(); for (int i = 0; i < c->count_modes; ++i) { bool exists = false; diff --git a/drm/DrmDevice.cpp b/drm/DrmDevice.cpp index a679bf5..8245b78 100644 --- a/drm/DrmDevice.cpp +++ b/drm/DrmDevice.cpp @@ -331,9 +331,6 @@ std::tuple<int, int> DrmDevice::Init(const char *path, int num_displays) { ALOGE("Failed CreateDisplayPipe %d with %d", conn->id(), ret); return std::make_tuple(ret, 0); } - if (!AttachWriteback(conn.get())) { - ALOGI("Display %d has writeback attach to it", conn->display()); - } } return std::make_tuple(ret, displays_.size()); } @@ -350,39 +347,6 @@ DrmConnector *DrmDevice::GetConnectorForDisplay(int display) const { return nullptr; } -DrmConnector *DrmDevice::GetWritebackConnectorForDisplay(int display) const { - for (const auto &conn : writeback_connectors_) { - if (conn->display() == display) - return conn.get(); - } - return nullptr; -} - -// TODO(nobody): what happens when hotplugging -DrmConnector *DrmDevice::AvailableWritebackConnector(int display) const { - DrmConnector *writeback_conn = GetWritebackConnectorForDisplay(display); - DrmConnector *display_conn = GetConnectorForDisplay(display); - // If we have a writeback already attached to the same CRTC just use that, - // if possible. - if (display_conn && writeback_conn && - writeback_conn->encoder()->CanClone(display_conn->encoder())) - return writeback_conn; - - // Use another CRTC if available and doesn't have any connector - for (const auto &crtc : crtcs_) { - if (crtc->display() == display) - continue; - display_conn = GetConnectorForDisplay(crtc->display()); - // If we have a display connected don't use it for writeback - if (display_conn && display_conn->state() == DRM_MODE_CONNECTED) - continue; - writeback_conn = GetWritebackConnectorForDisplay(crtc->display()); - if (writeback_conn) - return writeback_conn; - } - return nullptr; -} - DrmCrtc *DrmDevice::GetCrtcForDisplay(int display) const { for (const auto &crtc : crtcs_) { if (crtc->display() == display) @@ -457,34 +421,6 @@ int DrmDevice::CreateDisplayPipe(DrmConnector *connector) { return -ENODEV; } -// Attach writeback connector to the CRTC linked to the display_conn -int DrmDevice::AttachWriteback(DrmConnector *display_conn) { - DrmCrtc *display_crtc = display_conn->encoder()->crtc(); - if (GetWritebackConnectorForDisplay(display_crtc->display()) != nullptr) { - ALOGE("Display already has writeback attach to it"); - return -EINVAL; - } - for (auto &writeback_conn : writeback_connectors_) { - if (writeback_conn->display() >= 0) - continue; - for (DrmEncoder *writeback_enc : writeback_conn->possible_encoders()) { - for (DrmCrtc *possible_crtc : writeback_enc->possible_crtcs()) { - if (possible_crtc != display_crtc) - continue; - // Use just encoders which had not been bound already - if (writeback_enc->can_bind(display_crtc->display())) { - writeback_enc->set_crtc(display_crtc); - writeback_conn->set_encoder(writeback_enc); - writeback_conn->set_display(display_crtc->display()); - writeback_conn->UpdateModes(); - return 0; - } - } - } - } - return -EINVAL; -} - auto DrmDevice::RegisterUserPropertyBlob(void *data, size_t length) const -> DrmModeUserPropertyBlobUnique { struct drm_mode_create_blob create_blob {}; @@ -494,7 +430,7 @@ auto DrmDevice::RegisterUserPropertyBlob(void *data, size_t length) const int ret = drmIoctl(fd(), DRM_IOCTL_MODE_CREATEPROPBLOB, &create_blob); if (ret) { ALOGE("Failed to create mode property blob %d", ret); - return DrmModeUserPropertyBlobUnique(); + return {}; } return DrmModeUserPropertyBlobUnique( diff --git a/drm/DrmDevice.h b/drm/DrmDevice.h index d3effc2..9983d61 100644 --- a/drm/DrmDevice.h +++ b/drm/DrmDevice.h @@ -60,8 +60,6 @@ class DrmDevice { } DrmConnector *GetConnectorForDisplay(int display) const; - DrmConnector *GetWritebackConnectorForDisplay(int display) const; - DrmConnector *AvailableWritebackConnector(int display) const; DrmCrtc *GetCrtcForDisplay(int display) const; int GetCrtcProperty(const DrmCrtc &crtc, const char *prop_name, @@ -96,7 +94,6 @@ class DrmDevice { int TryEncoderForDisplay(int display, DrmEncoder *enc); int CreateDisplayPipe(DrmConnector *connector); - int AttachWriteback(DrmConnector *display_conn); UniqueFd fd_; uint32_t mode_id_ = 0; diff --git a/drm/DrmFbImporter.cpp b/drm/DrmFbImporter.cpp index 25f32b7..6f2abe8 100644 --- a/drm/DrmFbImporter.cpp +++ b/drm/DrmFbImporter.cpp @@ -124,7 +124,7 @@ auto DrmFbImporter::GetOrCreateFbId(hwc_drm_bo_t *bo) if (err != 0) { ALOGE("Failed to import prime fd %d ret=%d", bo->prime_fds[0], err); - return std::shared_ptr<DrmFbIdHandle>(); + return {}; } auto drm_fb_id_cached = drm_fb_id_handle_cache_.find(first_handle); diff --git a/drm/DrmMode.cpp b/drm/DrmMode.cpp index 1c8bd0f..010ea1b 100644 --- a/drm/DrmMode.cpp +++ b/drm/DrmMode.cpp @@ -94,6 +94,9 @@ uint16_t DrmMode::v_scan() const { } float DrmMode::v_refresh() const { + if (clock_ == 0) { + return v_refresh_; + } // Always recalculate refresh to report correct float rate return static_cast<float>(clock_) / (float)(v_total_ * h_total_) * 1000.0F; } diff --git a/drm/ResourceManager.h b/drm/ResourceManager.h index 773b350..f54d682 100644 --- a/drm/ResourceManager.h +++ b/drm/ResourceManager.h @@ -48,6 +48,10 @@ class ResourceManager { return &uevent_listener_; } + auto &GetMainLock() { + return main_lock_; + } + private: int AddDrmDevice(std::string const &path); @@ -57,6 +61,8 @@ class ResourceManager { bool scale_with_gpu_{}; UEventListener uevent_listener_; + + std::mutex main_lock_; }; } // namespace android diff --git a/drm/UEventListener.cpp b/drm/UEventListener.cpp index 470e89a..44c503d 100644 --- a/drm/UEventListener.cpp +++ b/drm/UEventListener.cpp @@ -90,6 +90,10 @@ void UEventListener::Routine() { } if (drm_event && hotplug_event && hotplug_handler_) { + constexpr useconds_t delay_after_uevent_us = 200000; + /* We need some delay to ensure DrmConnector::UpdateModes() will query + * correct modes list, otherwise at least RPI4 board may report 0 modes */ + usleep(delay_after_uevent_us); hotplug_handler_(); } } diff --git a/drm/VSyncWorker.cpp b/drm/VSyncWorker.cpp index 6e92838..8d1cb99 100644 --- a/drm/VSyncWorker.cpp +++ b/drm/VSyncWorker.cpp @@ -77,7 +77,7 @@ int64_t VSyncWorker::GetPhasedVSync(int64_t frame_ns, int64_t current) const { last_timestamp_; } -static const int64_t kOneSecondNs = 1 * 1000 * 1000 * 1000; +static const int64_t kOneSecondNs = 1LL * 1000 * 1000 * 1000; int VSyncWorker::SyntheticWaitVBlank(int64_t *timestamp) { struct timespec vsync {}; diff --git a/hwc2_device/DrmHwcTwo.cpp b/hwc2_device/DrmHwcTwo.cpp index 5bbb48a..a2a093f 100644 --- a/hwc2_device/DrmHwcTwo.cpp +++ b/hwc2_device/DrmHwcTwo.cpp @@ -66,8 +66,11 @@ HWC2::Error DrmHwcTwo::Init() { } } - resource_manager_.GetUEventListener()->RegisterHotplugHandler( - [this] { HandleHotplugUEvent(); }); + resource_manager_.GetUEventListener()->RegisterHotplugHandler([this] { + const std::lock_guard<std::mutex> lock(GetResMan().GetMainLock()); + + HandleHotplugUEvent(); + }); return ret; } @@ -111,12 +114,9 @@ uint32_t DrmHwcTwo::GetMaxVirtualDisplayCount() { HWC2::Error DrmHwcTwo::RegisterCallback(int32_t descriptor, hwc2_callback_data_t data, hwc2_function_pointer_t function) { - std::unique_lock<std::mutex> lock(callback_lock_); - switch (static_cast<HWC2::Callback>(descriptor)) { case HWC2::Callback::Hotplug: { hotplug_callback_ = std::make_pair(HWC2_PFN_HOTPLUG(function), data); - lock.unlock(); const auto &drm_devices = resource_manager_.GetDrmDevices(); for (const auto &device : drm_devices) HandleInitialHotplugState(device.get()); @@ -143,22 +143,36 @@ HWC2::Error DrmHwcTwo::RegisterCallback(int32_t descriptor, } void DrmHwcTwo::HandleDisplayHotplug(hwc2_display_t displayid, int state) { - const std::lock_guard<std::mutex> lock(callback_lock_); - - if (hotplug_callback_.first != nullptr && - hotplug_callback_.second != nullptr) { - hotplug_callback_.first(hotplug_callback_.second, displayid, - state == DRM_MODE_CONNECTED - ? HWC2_CONNECTION_CONNECTED - : HWC2_CONNECTION_DISCONNECTED); + auto &mutex = GetResMan().GetMainLock(); + if (mutex.try_lock()) { + ALOGE("FIXME!!!: Main mutex must be locked in %s", __func__); + mutex.unlock(); + return; + } + + auto hc = hotplug_callback_; + if (hc.first != nullptr && hc.second != nullptr) { + /* For some reason CLIENT will call HWC2 API in hotplug callback handler, + * which will cause deadlock . Unlock main mutex to prevent this. + */ + mutex.unlock(); + hc.first(hc.second, displayid, + state == DRM_MODE_CONNECTED ? HWC2_CONNECTION_CONNECTED + : HWC2_CONNECTION_DISCONNECTED); + mutex.lock(); } } void DrmHwcTwo::HandleInitialHotplugState(DrmDevice *drmDevice) { for (const auto &conn : drmDevice->connectors()) { - if (conn->state() != DRM_MODE_CONNECTED) + int display_id = conn->display(); + auto &display = displays_.at(display_id); + + if (conn->state() != DRM_MODE_CONNECTED && !display.IsInHeadlessMode()) continue; - HandleDisplayHotplug(conn->display(), conn->state()); + HandleDisplayHotplug(conn->display(), display.IsInHeadlessMode() + ? DRM_MODE_CONNECTED + : conn->state()); } } @@ -178,15 +192,15 @@ void DrmHwcTwo::HandleHotplugUEvent() { conn->display()); int display_id = conn->display(); - if (cur_state == DRM_MODE_CONNECTED) { - auto &display = displays_.at(display_id); - display.ChosePreferredConfig(); - } else { - auto &display = displays_.at(display_id); + auto &display = displays_.at(display_id); + display.ChosePreferredConfig(); + if (cur_state != DRM_MODE_CONNECTED) { display.ClearDisplay(); } - HandleDisplayHotplug(display_id, cur_state); + HandleDisplayHotplug(display_id, display.IsInHeadlessMode() + ? DRM_MODE_CONNECTED + : cur_state); } } } diff --git a/hwc2_device/DrmHwcTwo.h b/hwc2_device/DrmHwcTwo.h index d096160..f38ba05 100644 --- a/hwc2_device/DrmHwcTwo.h +++ b/hwc2_device/DrmHwcTwo.h @@ -37,8 +37,6 @@ class DrmHwcTwo { #endif std::pair<HWC2_PFN_REFRESH, hwc2_callback_data_t> refresh_callback_{}; - std::mutex callback_lock_; - static HwcDisplay *GetDisplay(DrmHwcTwo *hwc, hwc2_display_t display_handle) { auto it = hwc->displays_.find(display_handle); if (it == hwc->displays_.end()) @@ -57,6 +55,10 @@ class DrmHwcTwo { hwc2_function_pointer_t function); HWC2::Error CreateDisplay(hwc2_display_t displ, HWC2::DisplayType type); + auto &GetResMan() { + return resource_manager_; + } + private: void HandleDisplayHotplug(hwc2_display_t displayid, int state); void HandleInitialHotplugState(DrmDevice *drmDevice); diff --git a/hwc2_device/HwcDisplay.cpp b/hwc2_device/HwcDisplay.cpp index 9055a9a..8936136 100644 --- a/hwc2_device/HwcDisplay.cpp +++ b/hwc2_device/HwcDisplay.cpp @@ -98,6 +98,11 @@ HwcDisplay::HwcDisplay(ResourceManager *resource_manager, DrmDevice *drm, } void HwcDisplay::ClearDisplay() { + if (IsInHeadlessMode()) { + ALOGE("%s: Headless mode, should never reach here: ", __func__); + return; + } + AtomicCommitArgs a_args = {.clear_active_composition = true}; compositor_.ExecuteAtomicCommit(a_args); } @@ -136,7 +141,7 @@ HWC2::Error HwcDisplay::Init(std::vector<DrmPlane *> *planes) { } ret = vsync_worker_.Init(drm_, display, [this](int64_t timestamp) { - const std::lock_guard<std::mutex> lock(hwc2_->callback_lock_); + const std::lock_guard<std::mutex> lock(hwc2_->GetResMan().GetMainLock()); /* vsync callback */ #if PLATFORM_SDK_VERSION > 29 if (hwc2_->vsync_2_4_callback_.first != nullptr && @@ -160,7 +165,8 @@ HWC2::Error HwcDisplay::Init(std::vector<DrmPlane *> *planes) { ret = flattening_vsync_worker_ .Init(drm_, display, [this](int64_t /*timestamp*/) { - const std::lock_guard<std::mutex> lock(hwc2_->callback_lock_); + const std::lock_guard<std::mutex> lock( + hwc2_->GetResMan().GetMainLock()); /* Frontend flattening */ if (flattenning_state_ > ClientFlattenningState::ClientRefreshRequested && @@ -191,7 +197,7 @@ HWC2::Error HwcDisplay::Init(std::vector<DrmPlane *> *planes) { HWC2::Error HwcDisplay::ChosePreferredConfig() { HWC2::Error err = configs_.Update(*connector_); - if (err != HWC2::Error::None) + if (!IsInHeadlessMode() && err != HWC2::Error::None) return HWC2::Error::BadDisplay; return SetActiveConfig(configs_.preferred_config_id); @@ -229,6 +235,11 @@ HWC2::Error HwcDisplay::GetActiveConfig(hwc2_config_t *config) const { HWC2::Error HwcDisplay::GetChangedCompositionTypes(uint32_t *num_elements, hwc2_layer_t *layers, int32_t *types) { + if (IsInHeadlessMode()) { + *num_elements = 0; + return HWC2::Error::None; + } + uint32_t num_changes = 0; for (std::pair<const hwc2_layer_t, HwcLayer> &l : layers_) { if (l.second.IsTypeChanged()) { @@ -249,6 +260,9 @@ HWC2::Error HwcDisplay::GetClientTargetSupport(uint32_t width, uint32_t height, int32_t dataspace) { std::pair<uint32_t, uint32_t> min = drm_->min_resolution(); std::pair<uint32_t, uint32_t> max = drm_->max_resolution(); + if (IsInHeadlessMode()) { + return HWC2::Error::None; + } if (width < min.first || height < min.second) return HWC2::Error::Unsupported; @@ -286,8 +300,8 @@ HWC2::Error HwcDisplay::GetDisplayAttribute(hwc2_config_t config, auto &hwc_config = configs_.hwc_configs[conf]; static const int32_t kUmPerInch = 25400; - uint32_t mm_width = connector_->mm_width(); - uint32_t mm_height = connector_->mm_height(); + uint32_t mm_width = configs_.mm_width; + uint32_t mm_height = configs_.mm_height; auto attribute = static_cast<HWC2::Attribute>(attribute_in); switch (attribute) { case HWC2::Attribute::Width: @@ -328,15 +342,6 @@ HWC2::Error HwcDisplay::GetDisplayAttribute(hwc2_config_t config, HWC2::Error HwcDisplay::GetDisplayConfigs(uint32_t *num_configs, hwc2_config_t *configs) { - // Since this callback is normally invoked twice (once to get the count, and - // once to populate configs), we don't really want to read the edid - // redundantly. Instead, only update the modes on the first invocation. While - // it's possible this will result in stale modes, it'll all come out in the - // wash when we try to set the active config later. - if (!configs) { - configs_.Update(*connector_); - } - uint32_t idx = 0; for (auto &hwc_config : configs_.hwc_configs) { if (hwc_config.second.disabled) { @@ -406,6 +411,11 @@ HWC2::Error HwcDisplay::GetHdrCapabilities(uint32_t *num_types, HWC2::Error HwcDisplay::GetReleaseFences(uint32_t *num_elements, hwc2_layer_t *layers, int32_t *fences) { + if (IsInHeadlessMode()) { + *num_elements = 0; + return HWC2::Error::None; + } + uint32_t num_layers = 0; for (std::pair<const hwc2_layer_t, HwcLayer> &l : layers_) { @@ -426,6 +436,11 @@ HWC2::Error HwcDisplay::GetReleaseFences(uint32_t *num_elements, } HWC2::Error HwcDisplay::CreateComposition(AtomicCommitArgs &a_args) { + if (IsInHeadlessMode()) { + ALOGE("%s: Display is in headless mode, should never reach here", __func__); + return HWC2::Error::None; + } + // order the layers by z-order bool use_client_layer = false; uint32_t client_z_order = UINT32_MAX; @@ -505,7 +520,11 @@ HWC2::Error HwcDisplay::CreateComposition(AtomicCommitArgs &a_args) { * https://cs.android.com/android/platform/superproject/+/android-11.0.0_r3:hardware/libhardware/include/hardware/hwcomposer2.h;l=1805 */ HWC2::Error HwcDisplay::PresentDisplay(int32_t *present_fence) { - HWC2::Error ret; + if (IsInHeadlessMode()) { + *present_fence = -1; + return HWC2::Error::None; + } + HWC2::Error ret{}; ++total_stats_.total_frames_; @@ -619,6 +638,10 @@ HWC2::Error HwcDisplay::SetOutputBuffer(buffer_handle_t /*buffer*/, } HWC2::Error HwcDisplay::SetPowerMode(int32_t mode_in) { + if (IsInHeadlessMode()) { + return HWC2::Error::None; + } + auto mode = static_cast<HWC2::PowerMode>(mode_in); AtomicCommitArgs a_args{}; @@ -660,6 +683,10 @@ HWC2::Error HwcDisplay::SetVsyncEnabled(int32_t enabled) { HWC2::Error HwcDisplay::ValidateDisplay(uint32_t *num_types, uint32_t *num_requests) { + if (IsInHeadlessMode()) { + *num_types = *num_requests = 0; + return HWC2::Error::None; + } return backend_->ValidateDisplay(this, num_types, num_requests); } diff --git a/hwc2_device/HwcDisplay.h b/hwc2_device/HwcDisplay.h index 6f46f5d..c3e0f6e 100644 --- a/hwc2_device/HwcDisplay.h +++ b/hwc2_device/HwcDisplay.h @@ -200,6 +200,16 @@ class HwcDisplay { return false; } + /* Headless mode required to keep SurfaceFlinger alive when all display are + * disconnected, Without headless mode Android will continuously crash. + * Only single internal (primary) display is required to be in HEADLESS mode + * to prevent the crash. See: + * https://source.android.com/devices/graphics/hotplug#handling-common-scenarios + */ + bool IsInHeadlessMode() { + return handle_ == 0 && connector_->state() != DRM_MODE_CONNECTED; + } + private: enum ClientFlattenningState : int32_t { Disabled = -3, diff --git a/hwc2_device/HwcDisplayConfigs.cpp b/hwc2_device/HwcDisplayConfigs.cpp index d1a8d4c..16f1ed0 100644 --- a/hwc2_device/HwcDisplayConfigs.cpp +++ b/hwc2_device/HwcDisplayConfigs.cpp @@ -23,26 +23,57 @@ #include "drm/DrmConnector.h" #include "utils/log.h" +constexpr uint32_t kHeadlessModeDisplayWidthMm = 163; +constexpr uint32_t kHeadlessModeDisplayHeightMm = 122; +constexpr uint32_t kHeadlessModeDisplayWidthPx = 1024; +constexpr uint32_t kHeadlessModeDisplayHeightPx = 768; +constexpr uint32_t kHeadlessModeDisplayVRefresh = 60; + namespace android { // NOLINTNEXTLINE (readability-function-cognitive-complexity): Fixme HWC2::Error HwcDisplayConfigs::Update(DrmConnector &connector) { + /* In case UpdateModes will fail we will still have one mode for headless + * mode*/ + hwc_configs.clear(); + + last_config_id++; + preferred_config_id = active_config_id = last_config_id; + auto headless_drm_mode_info = (drmModeModeInfo){ + .hdisplay = kHeadlessModeDisplayWidthPx, + .vdisplay = kHeadlessModeDisplayHeightPx, + .vrefresh = kHeadlessModeDisplayVRefresh, + .name = "HEADLESS-MODE", + }; + hwc_configs[active_config_id] = (HwcDisplayConfig){ + .id = active_config_id, + .group_id = 1, + .mode = DrmMode(&headless_drm_mode_info), + }; + + mm_width = kHeadlessModeDisplayWidthMm; + mm_height = kHeadlessModeDisplayHeightMm; + + /* Read real configs */ int ret = connector.UpdateModes(); if (ret != 0) { ALOGE("Failed to update display modes %d", ret); return HWC2::Error::BadDisplay; } - hwc_configs.clear(); - preferred_config_id = 0; - int preferred_config_group_id = 0; - if (connector.modes().empty()) { ALOGE("No modes reported by KMS"); return HWC2::Error::BadDisplay; } - int last_config_id = 1; + hwc_configs.clear(); + mm_width = connector.mm_width(); + mm_height = connector.mm_height(); + + preferred_config_id = 0; + int preferred_config_group_id = 0; + + int first_config_id = last_config_id; int last_group_id = 1; /* Group modes */ @@ -87,7 +118,7 @@ HWC2::Error HwcDisplayConfigs::Update(DrmConnector &connector) { /* We must have preferred mode. Set first mode as preferred * in case KMS haven't reported anything. */ if (preferred_config_id == 0) { - preferred_config_id = 1; + preferred_config_id = first_config_id; preferred_config_group_id = 1; } @@ -142,8 +173,8 @@ HWC2::Error HwcDisplayConfigs::Update(DrmConnector &connector) { * otherwise android.graphics.cts.SetFrameRateTest CTS will fail */ constexpr float kMinFpsDelta = 1.0; // FPS - for (int m1 = 1; m1 < last_config_id; m1++) { - for (int m2 = 1; m2 < last_config_id; m2++) { + for (int m1 = first_config_id; m1 < last_config_id; m1++) { + for (int m2 = first_config_id; m2 < last_config_id; m2++) { if (m1 != m2 && hwc_configs[m1].group_id == hwc_configs[m2].group_id && !hwc_configs[m1].disabled && !hwc_configs[m2].disabled && fabsf(hwc_configs[m1].mode.v_refresh() - @@ -159,6 +190,8 @@ HWC2::Error HwcDisplayConfigs::Update(DrmConnector &connector) { } } + /* Set active mode to be valid mode */ + active_config_id = preferred_config_id; return HWC2::Error::None; } diff --git a/hwc2_device/HwcDisplayConfigs.h b/hwc2_device/HwcDisplayConfigs.h index cb38625..5bcf696 100644 --- a/hwc2_device/HwcDisplayConfigs.h +++ b/hwc2_device/HwcDisplayConfigs.h @@ -45,6 +45,11 @@ struct HwcDisplayConfigs { int active_config_id = 0; int preferred_config_id = 0; + + int last_config_id = 1; + + uint32_t mm_width = 0; + uint32_t mm_height = 0; }; } // namespace android diff --git a/hwc2_device/hwc2_device.cpp b/hwc2_device/hwc2_device.cpp index 22e4589..6d258e8 100644 --- a/hwc2_device/hwc2_device.cpp +++ b/hwc2_device/hwc2_device.cpp @@ -14,14 +14,35 @@ * limitations under the License. */ +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +// #define LOG_NDEBUG 0 // Uncomment to see HWC2 API calls in logcat + #define LOG_TAG "hwc2-device" +#include <cinttypes> + #include "DrmHwcTwo.h" #include "backend/Backend.h" #include "utils/log.h" namespace android { +/* Converts long __PRETTY_FUNCTION__ result, e.g.: + * "int32_t android::LayerHook(hwc2_device_t *, hwc2_display_t, hwc2_layer_t," + * "Args...) [HookType = HWC2::Error (android::HwcLayer::*)(const native_handle" + * "*,int), func = &android::HwcLayer::SetLayerBuffer, Args = <const + * "native_handle, int>" + * to the short "android::HwcLayer::SetLayerBuffer" for better logs readability + */ +static std::string GetFuncName(const char *pretty_function) { + std::string str(pretty_function); + const char *start = "func = &"; + size_t p1 = str.find(start); + p1 += strlen(start); + size_t p2 = str.find(',', p1); + return str.substr(p1, p2 - p1); +} + struct Drmhwc2Device : hwc2_device { DrmHwcTwo drmhwctwo; }; @@ -40,14 +61,20 @@ static hwc2_function_pointer_t ToHook(T function) { template <typename T, typename HookType, HookType func, typename... Args> static T DeviceHook(hwc2_device_t *dev, Args... args) { + ALOGV("Device hook: %s", GetFuncName(__PRETTY_FUNCTION__).c_str()); DrmHwcTwo *hwc = ToDrmHwcTwo(dev); + const std::lock_guard<std::mutex> lock(hwc->GetResMan().GetMainLock()); return static_cast<T>(((*hwc).*func)(std::forward<Args>(args)...)); } template <typename HookType, HookType func, typename... Args> static int32_t DisplayHook(hwc2_device_t *dev, hwc2_display_t display_handle, Args... args) { - HwcDisplay *display = DrmHwcTwo::GetDisplay(ToDrmHwcTwo(dev), display_handle); + ALOGV("Display #%" PRIu64 " hook: %s", display_handle, + GetFuncName(__PRETTY_FUNCTION__).c_str()); + DrmHwcTwo *hwc = ToDrmHwcTwo(dev); + const std::lock_guard<std::mutex> lock(hwc->GetResMan().GetMainLock()); + HwcDisplay *display = DrmHwcTwo::GetDisplay(hwc, display_handle); if (!display) return static_cast<int32_t>(HWC2::Error::BadDisplay); @@ -57,7 +84,11 @@ static int32_t DisplayHook(hwc2_device_t *dev, hwc2_display_t display_handle, template <typename HookType, HookType func, typename... Args> static int32_t LayerHook(hwc2_device_t *dev, hwc2_display_t display_handle, hwc2_layer_t layer_handle, Args... args) { - HwcDisplay *display = DrmHwcTwo::GetDisplay(ToDrmHwcTwo(dev), display_handle); + ALOGV("Display #%" PRIu64 " Layer: #%" PRIu64 " hook: %s", display_handle, + layer_handle, GetFuncName(__PRETTY_FUNCTION__).c_str()); + DrmHwcTwo *hwc = ToDrmHwcTwo(dev); + const std::lock_guard<std::mutex> lock(hwc->GetResMan().GetMainLock()); + HwcDisplay *display = DrmHwcTwo::GetDisplay(hwc, display_handle); if (!display) return static_cast<int32_t>(HWC2::Error::BadDisplay); diff --git a/utils/autolock.cpp b/utils/autolock.cpp deleted file mode 100644 index 3afe488..0000000 --- a/utils/autolock.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define ATRACE_TAG ATRACE_TAG_GRAPHICS -#define LOG_TAG "hwc-drm-auto-lock" - -#include "autolock.h" - -#include <pthread.h> - -#include <cerrno> - -#include "utils/log.h" - -namespace android { - -int AutoLock::Lock() { - if (locked_) { - ALOGE("Invalid attempt to double lock AutoLock %s", name_); - return -EINVAL; - } - int ret = pthread_mutex_lock(mutex_); - if (ret != 0) { - ALOGE("Failed to acquire %s lock %d", name_, ret); - return ret; - } - locked_ = true; - return 0; -} - -int AutoLock::Unlock() { - if (!locked_) { - ALOGE("Invalid attempt to unlock unlocked AutoLock %s", name_); - return -EINVAL; - } - int ret = pthread_mutex_unlock(mutex_); - if (ret != 0) { - ALOGE("Failed to release %s lock %d", name_, ret); - return ret; - } - locked_ = false; - return 0; -} -} // namespace android diff --git a/utils/autolock.h b/utils/autolock.h deleted file mode 100644 index 006406a..0000000 --- a/utils/autolock.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <pthread.h> - -namespace android { - -class AutoLock { - public: - AutoLock(pthread_mutex_t *mutex, const char *const name) - : mutex_(mutex), name_(name) { - } - ~AutoLock() { - if (locked_) - Unlock(); - } - - AutoLock(const AutoLock &rhs) = delete; - AutoLock &operator=(const AutoLock &rhs) = delete; - - int Lock(); - int Unlock(); - - private: - pthread_mutex_t *const mutex_; - bool locked_ = false; - const char *const name_; -}; -} // namespace android |