diff --git a/compositor/DisplayInfo.h b/compositor/DisplayInfo.h index bbcbff8..6ddc66f 100644 --- a/compositor/DisplayInfo.h +++ b/compositor/DisplayInfo.h @@ -46,3 +46,12 @@ enum PanelOrientation { kModePanelOrientationLeftUp, kModePanelOrientationRightUp }; + +struct QueuedConfigTiming { + // In order for the new config to be applied, the client must send a new frame + // at this time. + int64_t refresh_time_ns; + + // The time when the display will start to refresh at the new vsync period. + int64_t new_vsync_time_ns; +}; diff --git a/hwc2_device/HwcDisplay.cpp b/hwc2_device/HwcDisplay.cpp index ec481e1..ffe6b11 100644 --- a/hwc2_device/HwcDisplay.cpp +++ b/hwc2_device/HwcDisplay.cpp @@ -96,7 +96,7 @@ HwcDisplay::~HwcDisplay() { Deinit(); }; -const HwcDisplayConfig *HwcDisplay::GetCurrentConfig() const { +auto HwcDisplay::GetCurrentConfig() const -> const HwcDisplayConfig * { auto config_iter = configs_.hwc_configs.find(configs_.active_config_id); if (config_iter == configs_.hwc_configs.end()) { return nullptr; @@ -104,7 +104,7 @@ const HwcDisplayConfig *HwcDisplay::GetCurrentConfig() const { return &config_iter->second; } -const HwcDisplayConfig *HwcDisplay::GetLastRequestedConfig() const { +auto HwcDisplay::GetLastRequestedConfig() const -> const HwcDisplayConfig * { auto config_iter = configs_.hwc_configs.find(staged_mode_config_id_); if (config_iter == configs_.hwc_configs.end()) { return nullptr; @@ -112,6 +112,42 @@ const HwcDisplayConfig *HwcDisplay::GetLastRequestedConfig() const { return &config_iter->second; } +auto HwcDisplay::QueueConfig(hwc2_config_t config, int64_t desired_time, + bool seamless, QueuedConfigTiming *out_timing) + -> ConfigError { + if (configs_.hwc_configs.count(config) == 0) { + ALOGE("Could not find active mode for %u", config); + return ConfigError::kBadConfig; + } + + // TODO: Add support for seamless configuration changes. + if (seamless) { + return ConfigError::kSeamlessNotAllowed; + } + + // Request a refresh from the client one vsync period before the desired + // time, or simply at the desired time if there is no active configuration. + const HwcDisplayConfig *current_config = GetCurrentConfig(); + out_timing->refresh_time_ns = desired_time - + (current_config + ? current_config->mode.GetVSyncPeriodNs() + : 0); + out_timing->new_vsync_time_ns = desired_time; + + // Queue the config change timing to be consistent with the requested + // refresh time. + staged_mode_ = configs_.hwc_configs[config].mode; + staged_mode_change_time_ = out_timing->refresh_time_ns; + staged_mode_config_id_ = config; + + // Enable vsync events until the mode has been applied. + last_vsync_ts_ = 0; + vsync_tracking_en_ = true; + vsync_worker_->VSyncControl(true); + + return ConfigError::kNone; +} + void HwcDisplay::SetPipeline(std::shared_ptr pipeline) { Deinit(); diff --git a/hwc2_device/HwcDisplay.h b/hwc2_device/HwcDisplay.h index a315540..91e5df7 100644 --- a/hwc2_device/HwcDisplay.h +++ b/hwc2_device/HwcDisplay.h @@ -41,6 +41,13 @@ inline constexpr uint32_t kPrimaryDisplay = 0; // NOLINTNEXTLINE class HwcDisplay { public: + enum ConfigError { + kNone, + kBadConfig, + kSeamlessNotAllowed, + kSeamlessNotPossible + }; + HwcDisplay(hwc2_display_t handle, HWC2::DisplayType type, DrmHwc *hwc); HwcDisplay(const HwcDisplay &) = delete; ~HwcDisplay(); @@ -60,12 +67,16 @@ class HwcDisplay { } // Get the config representing the mode that has been committed to KMS. - const HwcDisplayConfig *GetCurrentConfig() const; + auto GetCurrentConfig() const -> const HwcDisplayConfig *; // Get the config that was last requested through SetActiveConfig and similar // functions. This may differ from the GetCurrentConfig if the config change // is queued up to take effect in the future. - const HwcDisplayConfig *GetLastRequestedConfig() const; + auto GetLastRequestedConfig() const -> const HwcDisplayConfig *; + + // Queue a configuration change to take effect in the future. + auto QueueConfig(hwc2_config_t config, int64_t desired_time, bool seamless, + QueuedConfigTiming *out_timing) -> ConfigError; // HWC2 Hooks - these should not be used outside of the hwc2 device. HWC2::Error AcceptDisplayChanges(); diff --git a/hwc3/ComposerClient.cpp b/hwc3/ComposerClient.cpp index e2d5b21..fe520ed 100644 --- a/hwc3/ComposerClient.cpp +++ b/hwc3/ComposerClient.cpp @@ -1051,13 +1051,14 @@ ndk::ScopedAStatus ComposerClient::registerCallback( ndk::ScopedAStatus ComposerClient::setActiveConfig(int64_t display_id, int32_t config) { DEBUG_FUNC(); - const std::unique_lock lock(hwc_->GetResMan().GetMainLock()); - HwcDisplay* display = GetDisplay(display_id); - if (display == nullptr) { - return ToBinderStatus(hwc3::Error::kBadDisplay); - } - return ToBinderStatus(Hwc2toHwc3Error(display->SetActiveConfig(config))); + VsyncPeriodChangeTimeline timeline; + VsyncPeriodChangeConstraints constraints = { + .desiredTimeNanos = ::android::ResourceManager::GetTimeMonotonicNs(), + .seamlessRequired = false, + }; + return setActiveConfigWithConstraints(display_id, config, constraints, + &timeline); } ndk::ScopedAStatus ComposerClient::setActiveConfigWithConstraints( @@ -1071,23 +1072,24 @@ ndk::ScopedAStatus ComposerClient::setActiveConfigWithConstraints( return ToBinderStatus(hwc3::Error::kBadDisplay); } - hwc_vsync_period_change_constraints_t hwc2_constraints; - hwc2_constraints.desiredTimeNanos = constraints.desiredTimeNanos; - hwc2_constraints.seamlessRequired = static_cast( - constraints.seamlessRequired); + ::QueuedConfigTiming timing{}; + HwcDisplay::ConfigError + result = display->QueueConfig(config, constraints.desiredTimeNanos, + constraints.seamlessRequired, &timing); + timeline->newVsyncAppliedTimeNanos = timing.new_vsync_time_ns; + timeline->refreshTimeNanos = timing.refresh_time_ns; + timeline->refreshRequired = true; - hwc_vsync_period_change_timeline_t hwc2_timeline{}; - auto error = Hwc2toHwc3Error( - display->SetActiveConfigWithConstraints(config, &hwc2_constraints, - &hwc2_timeline)); - if (error != hwc3::Error::kNone) { - return ToBinderStatus(error); + switch (result) { + case HwcDisplay::ConfigError::kBadConfig: + return ToBinderStatus(hwc3::Error::kBadConfig); + case HwcDisplay::ConfigError::kSeamlessNotAllowed: + return ToBinderStatus(hwc3::Error::kSeamlessNotAllowed); + case HwcDisplay::ConfigError::kSeamlessNotPossible: + return ToBinderStatus(hwc3::Error::kSeamlessNotPossible); + case HwcDisplay::ConfigError::kNone: + return ndk::ScopedAStatus::ok(); } - - timeline->refreshTimeNanos = hwc2_timeline.refreshTimeNanos; - timeline->newVsyncAppliedTimeNanos = hwc2_timeline.newVsyncAppliedTimeNanos; - timeline->refreshRequired = static_cast(hwc2_timeline.refreshRequired); - return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus ComposerClient::setBootDisplayConfig(int64_t /*display_id*/,