diff options
Diffstat (limited to 'hwc2_device/HwcDisplay.cpp')
-rw-r--r-- | hwc2_device/HwcDisplay.cpp | 361 |
1 files changed, 208 insertions, 153 deletions
diff --git a/hwc2_device/HwcDisplay.cpp b/hwc2_device/HwcDisplay.cpp index 8936136..cedac19 100644 --- a/hwc2_device/HwcDisplay.cpp +++ b/hwc2_device/HwcDisplay.cpp @@ -20,6 +20,7 @@ #include "HwcDisplay.h" #include "DrmHwcTwo.h" +#include "backend/Backend.h" #include "backend/BackendManager.h" #include "bufferinfo/BufferInfoGetter.h" #include "utils/log.h" @@ -68,8 +69,12 @@ std::string HwcDisplay::Dump() { " VSync remains"; } + std::string connector_name = IsInHeadlessMode() + ? "NULL-DISPLAY" + : GetPipe().connector->Get()->GetName(); + std::stringstream ss; - ss << "- Display on: " << connector_->name() << "\n" + ss << "- Display on: " << connector_name << "\n" << " Flattening state: " << flattening_state_str << "\n" << "Statistics since system boot:\n" << DumpDelta(total_stats_) << "\n\n" @@ -80,12 +85,9 @@ std::string HwcDisplay::Dump() { return ss.str(); } -HwcDisplay::HwcDisplay(ResourceManager *resource_manager, DrmDevice *drm, - hwc2_display_t handle, HWC2::DisplayType type, +HwcDisplay::HwcDisplay(hwc2_display_t handle, HWC2::DisplayType type, DrmHwcTwo *hwc2) : hwc2_(hwc2), - resource_manager_(resource_manager), - drm_(drm), handle_(handle), type_(type), color_transform_hint_(HAL_COLOR_TRANSFORM_IDENTITY) { @@ -97,108 +99,72 @@ HwcDisplay::HwcDisplay(ResourceManager *resource_manager, DrmDevice *drm, // clang-format on } -void HwcDisplay::ClearDisplay() { - if (IsInHeadlessMode()) { - ALOGE("%s: Headless mode, should never reach here: ", __func__); - return; - } +HwcDisplay::~HwcDisplay() = default; - AtomicCommitArgs a_args = {.clear_active_composition = true}; - compositor_.ExecuteAtomicCommit(a_args); -} +void HwcDisplay::SetPipeline(DrmDisplayPipeline *pipeline) { + pipeline_ = pipeline; -HWC2::Error HwcDisplay::Init(std::vector<DrmPlane *> *planes) { - int display = static_cast<int>(handle_); - int ret = compositor_.Init(resource_manager_, display); - if (ret) { - ALOGE("Failed display compositor init for display %d (%d)", display, ret); - return HWC2::Error::NoResources; - } - - // Split up the given display planes into primary and overlay to properly - // interface with the composition - char use_overlay_planes_prop[PROPERTY_VALUE_MAX]; - property_get("vendor.hwc.drm.use_overlay_planes", use_overlay_planes_prop, - "1"); - bool use_overlay_planes = strtol(use_overlay_planes_prop, nullptr, 10); - for (auto &plane : *planes) { - if (plane->GetType() == DRM_PLANE_TYPE_PRIMARY) - primary_planes_.push_back(plane); - else if (use_overlay_planes && (plane)->GetType() == DRM_PLANE_TYPE_OVERLAY) - overlay_planes_.push_back(plane); - } - - crtc_ = drm_->GetCrtcForDisplay(display); - if (!crtc_) { - ALOGE("Failed to get crtc for display %d", display); - return HWC2::Error::BadDisplay; - } + if (pipeline != nullptr) { + ChosePreferredConfig(); + Init(); - connector_ = drm_->GetConnectorForDisplay(display); - if (!connector_) { - ALOGE("Failed to get connector for display %d", display); - return HWC2::Error::BadDisplay; + hwc2_->ScheduleHotplugEvent(handle_, /*connected = */ true); + } else { + backend_.reset(); + vsync_worker_.Init(nullptr, [](int64_t) {}); + SetClientTarget(nullptr, -1, 0, {}); + if (handle_ != kPrimaryDisplay) { + hwc2_->ScheduleHotplugEvent(handle_, /*connected = */ false); + } } +} - ret = vsync_worker_.Init(drm_, display, [this](int64_t timestamp) { +HWC2::Error HwcDisplay::Init() { + int ret = vsync_worker_.Init(pipeline_, [this](int64_t timestamp) { 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 && - hwc2_->vsync_2_4_callback_.second != nullptr) { - hwc2_vsync_period_t period_ns{}; + if (vsync_event_en_) { + uint32_t period_ns{}; GetDisplayVsyncPeriod(&period_ns); - hwc2_->vsync_2_4_callback_.first(hwc2_->vsync_2_4_callback_.second, - handle_, timestamp, period_ns); - } else -#endif - if (hwc2_->vsync_callback_.first != nullptr && - hwc2_->vsync_callback_.second != nullptr) { - hwc2_->vsync_callback_.first(hwc2_->vsync_callback_.second, handle_, - timestamp); + hwc2_->SendVsyncEventToClient(handle_, timestamp, period_ns); + } + if (vsync_flattening_en_) { + ProcessFlatenningVsyncInternal(); + } + if (vsync_tracking_en_) { + last_vsync_ts_ = timestamp; + } + if (!vsync_event_en_ && !vsync_flattening_en_ && !vsync_tracking_en_) { + vsync_worker_.VSyncControl(false); } }); - if (ret) { - ALOGE("Failed to create event worker for d=%d %d\n", display, ret); + if (ret && ret != -EALREADY) { + ALOGE("Failed to create event worker for d=%d %d\n", int(handle_), ret); return HWC2::Error::BadDisplay; } - ret = flattening_vsync_worker_ - .Init(drm_, display, [this](int64_t /*timestamp*/) { - const std::lock_guard<std::mutex> lock( - hwc2_->GetResMan().GetMainLock()); - /* Frontend flattening */ - if (flattenning_state_ > - ClientFlattenningState::ClientRefreshRequested && - --flattenning_state_ == - ClientFlattenningState::ClientRefreshRequested && - hwc2_->refresh_callback_.first != nullptr && - hwc2_->refresh_callback_.second != nullptr) { - hwc2_->refresh_callback_.first(hwc2_->refresh_callback_.second, - handle_); - flattening_vsync_worker_.VSyncControl(false); - } - }); - if (ret) { - ALOGE("Failed to create event worker for d=%d %d\n", display, ret); - return HWC2::Error::BadDisplay; - } - - ret = BackendManager::GetInstance().SetBackendForDisplay(this); - if (ret) { - ALOGE("Failed to set backend for d=%d %d\n", display, ret); - return HWC2::Error::BadDisplay; + if (!IsInHeadlessMode()) { + ret = BackendManager::GetInstance().SetBackendForDisplay(this); + if (ret) { + ALOGE("Failed to set backend for d=%d %d\n", int(handle_), ret); + return HWC2::Error::BadDisplay; + } } client_layer_.SetLayerBlendMode(HWC2_BLEND_MODE_PREMULTIPLIED); - return ChosePreferredConfig(); + return HWC2::Error::None; } HWC2::Error HwcDisplay::ChosePreferredConfig() { - HWC2::Error err = configs_.Update(*connector_); - if (!IsInHeadlessMode() && err != HWC2::Error::None) + HWC2::Error err{}; + if (!IsInHeadlessMode()) { + err = configs_.Update(*pipeline_->connector->Get()); + } else { + configs_.FillHeadless(); + } + if (!IsInHeadlessMode() && err != HWC2::Error::None) { return HWC2::Error::BadDisplay; + } return SetActiveConfig(configs_.preferred_config_id); } @@ -217,18 +183,19 @@ HWC2::Error HwcDisplay::CreateLayer(hwc2_layer_t *layer) { } HWC2::Error HwcDisplay::DestroyLayer(hwc2_layer_t layer) { - if (!get_layer(layer)) + if (!get_layer(layer)) { return HWC2::Error::BadLayer; + } layers_.erase(layer); return HWC2::Error::None; } HWC2::Error HwcDisplay::GetActiveConfig(hwc2_config_t *config) const { - if (configs_.hwc_configs.count(configs_.active_config_id) == 0) + if (configs_.hwc_configs.count(staged_mode_config_id_) == 0) return HWC2::Error::BadConfig; - *config = configs_.active_config_id; + *config = staged_mode_config_id_; return HWC2::Error::None; } @@ -258,12 +225,13 @@ HWC2::Error HwcDisplay::GetChangedCompositionTypes(uint32_t *num_elements, HWC2::Error HwcDisplay::GetClientTargetSupport(uint32_t width, uint32_t height, int32_t /*format*/, 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; } + std::pair<uint32_t, uint32_t> min = pipeline_->device->GetMinResolution(); + std::pair<uint32_t, uint32_t> max = pipeline_->device->GetMaxResolution(); + if (width < min.first || height < min.second) return HWC2::Error::Unsupported; @@ -293,7 +261,7 @@ HWC2::Error HwcDisplay::GetDisplayAttribute(hwc2_config_t config, int conf = static_cast<int>(config); if (configs_.hwc_configs.count(conf) == 0) { - ALOGE("Could not find active mode for %d", conf); + ALOGE("Could not find mode #%d", conf); return HWC2::Error::BadConfig; } @@ -330,7 +298,7 @@ HWC2::Error HwcDisplay::GetDisplayAttribute(hwc2_config_t config, case HWC2::Attribute::ConfigGroup: /* Dispite ConfigGroup is a part of HWC2.4 API, framework * able to request it even if service @2.1 is used */ - *value = hwc_config.group_id; + *value = int(hwc_config.group_id); break; #endif default: @@ -363,7 +331,11 @@ HWC2::Error HwcDisplay::GetDisplayConfigs(uint32_t *num_configs, HWC2::Error HwcDisplay::GetDisplayName(uint32_t *size, char *name) { std::ostringstream stream; - stream << "display-" << connector_->id(); + if (IsInHeadlessMode()) { + stream << "null-display"; + } else { + stream << "display-" << GetPipe().connector->Get()->GetId(); + } std::string string = stream.str(); size_t length = string.length(); if (!name) { @@ -441,6 +413,26 @@ HWC2::Error HwcDisplay::CreateComposition(AtomicCommitArgs &a_args) { return HWC2::Error::None; } + int PrevModeVsyncPeriodNs = static_cast<int>( + 1E9 / GetPipe().connector->Get()->GetActiveMode().v_refresh()); + + auto mode_update_commited_ = false; + if (staged_mode_ && + staged_mode_change_time_ <= ResourceManager::GetTimeMonotonicNs()) { + client_layer_.SetLayerDisplayFrame( + (hwc_rect_t){.left = 0, + .top = 0, + .right = static_cast<int>(staged_mode_->h_display()), + .bottom = static_cast<int>(staged_mode_->v_display())}); + + configs_.active_config_id = staged_mode_config_id_; + + a_args.display_mode = *staged_mode_; + if (!a_args.test_only) { + mode_update_commited_ = true; + } + } + // order the layers by z-order bool use_client_layer = false; uint32_t client_z_order = UINT32_MAX; @@ -471,7 +463,7 @@ HWC2::Error HwcDisplay::CreateComposition(AtomicCommitArgs &a_args) { for (std::pair<const uint32_t, HwcLayer *> &l : z_map) { DrmHwcLayer layer; l.second->PopulateDrmLayer(&layer); - int ret = layer.ImportBuffer(drm_); + int ret = layer.ImportBuffer(GetPipe().device); if (ret) { ALOGE("Failed to import layer, ret=%d", ret); return HWC2::Error::NoResources; @@ -479,29 +471,21 @@ HWC2::Error HwcDisplay::CreateComposition(AtomicCommitArgs &a_args) { composition_layers.emplace_back(std::move(layer)); } - auto composition = std::make_shared<DrmDisplayComposition>(crtc_); - - // TODO(nobody): Don't always assume geometry changed - int ret = composition->SetLayers(composition_layers.data(), - composition_layers.size()); - if (ret) { - ALOGE("Failed to set layers in the composition ret=%d", ret); - return HWC2::Error::BadLayer; - } - - std::vector<DrmPlane *> primary_planes(primary_planes_); - std::vector<DrmPlane *> overlay_planes(overlay_planes_); - ret = composition->Plan(&primary_planes, &overlay_planes); - if (ret) { - ALOGV("Failed to plan the composition ret=%d", ret); + /* Store plan to ensure shared planes won't be stolen by other display + * in between of ValidateDisplay() and PresentDisplay() calls + */ + current_plan_ = DrmKmsPlan::CreateDrmKmsPlan(GetPipe(), + std::move(composition_layers)); + if (!current_plan_) { + if (!a_args.test_only) { + ALOGE("Failed to create DrmKmsPlan"); + } return HWC2::Error::BadConfig; } - a_args.composition = composition; - if (staged_mode) { - a_args.display_mode = *staged_mode; - } - ret = compositor_.ExecuteAtomicCommit(a_args); + a_args.composition = current_plan_; + + int ret = GetPipe().atomic_state_manager->ExecuteAtomicCommit(a_args); if (ret) { if (!a_args.test_only) @@ -509,8 +493,13 @@ HWC2::Error HwcDisplay::CreateComposition(AtomicCommitArgs &a_args) { return HWC2::Error::BadParameter; } - if (!a_args.test_only) { - staged_mode.reset(); + if (mode_update_commited_) { + staged_mode_.reset(); + vsync_tracking_en_ = false; + if (last_vsync_ts_ != 0) { + hwc2_->SendVsyncPeriodTimingChangedEventToClient( + handle_, last_vsync_ts_ + PrevModeVsyncPeriodNs); + } } return HWC2::Error::None; @@ -548,30 +537,24 @@ HWC2::Error HwcDisplay::PresentDisplay(int32_t *present_fence) { return HWC2::Error::None; } -HWC2::Error HwcDisplay::SetActiveConfig(hwc2_config_t config) { - int conf = static_cast<int>(config); - - if (configs_.hwc_configs.count(conf) == 0) { - ALOGE("Could not find active mode for %d", conf); +HWC2::Error HwcDisplay::SetActiveConfigInternal(uint32_t config, + int64_t change_time) { + if (configs_.hwc_configs.count(config) == 0) { + ALOGE("Could not find active mode for %u", config); return HWC2::Error::BadConfig; } - auto &mode = configs_.hwc_configs[conf].mode; - - staged_mode = mode; - - configs_.active_config_id = conf; - - // Setup the client layer's dimensions - hwc_rect_t display_frame = {.left = 0, - .top = 0, - .right = static_cast<int>(mode.h_display()), - .bottom = static_cast<int>(mode.v_display())}; - client_layer_.SetLayerDisplayFrame(display_frame); + staged_mode_ = configs_.hwc_configs[config].mode; + staged_mode_change_time_ = change_time; + staged_mode_config_id_ = config; return HWC2::Error::None; } +HWC2::Error HwcDisplay::SetActiveConfig(hwc2_config_t config) { + return SetActiveConfigInternal(config, ResourceManager::GetTimeMonotonicNs()); +} + /* Find API details at: * https://cs.android.com/android/platform/superproject/+/android-11.0.0_r3:hardware/libhardware/include/hardware/hwcomposer2.h;l=1861 */ @@ -656,7 +639,7 @@ HWC2::Error HwcDisplay::SetPowerMode(int32_t mode_in) { * true, as the next composition frame will implicitly activate * the display */ - return compositor_.ActivateDisplayUsingDPMS() == 0 + return GetPipe().atomic_state_manager->ActivateDisplayUsingDPMS() == 0 ? HWC2::Error::None : HWC2::Error::BadParameter; break; @@ -668,7 +651,7 @@ HWC2::Error HwcDisplay::SetPowerMode(int32_t mode_in) { return HWC2::Error::BadParameter; }; - int err = compositor_.ExecuteAtomicCommit(a_args); + int err = GetPipe().atomic_state_manager->ExecuteAtomicCommit(a_args); if (err) { ALOGE("Failed to apply the dpms composition err=%d", err); return HWC2::Error::BadParameter; @@ -677,7 +660,10 @@ HWC2::Error HwcDisplay::SetPowerMode(int32_t mode_in) { } HWC2::Error HwcDisplay::SetVsyncEnabled(int32_t enabled) { - vsync_worker_.VSyncControl(HWC2_VSYNC_ENABLE == enabled); + vsync_event_en_ = HWC2_VSYNC_ENABLE == enabled; + if (vsync_event_en_) { + vsync_worker_.VSyncControl(true); + } return HWC2::Error::None; } @@ -706,11 +692,25 @@ std::vector<HwcLayer *> HwcDisplay::GetOrderLayersByZPos() { return ordered_layers; } +HWC2::Error HwcDisplay::GetDisplayVsyncPeriod( + uint32_t *outVsyncPeriod /* ns */) { + return GetDisplayAttribute(configs_.active_config_id, + HWC2_ATTRIBUTE_VSYNC_PERIOD, + (int32_t *)(outVsyncPeriod)); +} + #if PLATFORM_SDK_VERSION > 29 HWC2::Error HwcDisplay::GetDisplayConnectionType(uint32_t *outType) { - if (connector_->internal()) + if (IsInHeadlessMode()) { *outType = static_cast<uint32_t>(HWC2::DisplayConnectionType::Internal); - else if (connector_->external()) + return HWC2::Error::None; + } + /* Primary display should be always internal, + * otherwise SF will be unhappy and will crash + */ + if (GetPipe().connector->Get()->IsInternal() || handle_ == kPrimaryDisplay) + *outType = static_cast<uint32_t>(HWC2::DisplayConnectionType::Internal); + else if (GetPipe().connector->Get()->IsExternal()) *outType = static_cast<uint32_t>(HWC2::DisplayConnectionType::External); else return HWC2::Error::BadConfig; @@ -718,22 +718,38 @@ HWC2::Error HwcDisplay::GetDisplayConnectionType(uint32_t *outType) { return HWC2::Error::None; } -HWC2::Error HwcDisplay::GetDisplayVsyncPeriod( - hwc2_vsync_period_t *outVsyncPeriod /* ns */) { - return GetDisplayAttribute(configs_.active_config_id, - HWC2_ATTRIBUTE_VSYNC_PERIOD, - (int32_t *)(outVsyncPeriod)); -} - HWC2::Error HwcDisplay::SetActiveConfigWithConstraints( - hwc2_config_t /*config*/, + hwc2_config_t config, hwc_vsync_period_change_constraints_t *vsyncPeriodChangeConstraints, hwc_vsync_period_change_timeline_t *outTimeline) { if (vsyncPeriodChangeConstraints == nullptr || outTimeline == nullptr) { return HWC2::Error::BadParameter; } - return HWC2::Error::BadConfig; + uint32_t current_vsync_period{}; + GetDisplayVsyncPeriod(¤t_vsync_period); + + if (vsyncPeriodChangeConstraints->seamlessRequired) { + return HWC2::Error::SeamlessNotAllowed; + } + + outTimeline->refreshTimeNanos = vsyncPeriodChangeConstraints + ->desiredTimeNanos - + current_vsync_period; + auto ret = SetActiveConfigInternal(config, outTimeline->refreshTimeNanos); + if (ret != HWC2::Error::None) { + return ret; + } + + outTimeline->refreshRequired = true; + outTimeline->newVsyncAppliedTimeNanos = vsyncPeriodChangeConstraints + ->desiredTimeNanos; + + last_vsync_ts_ = 0; + vsync_tracking_en_ = true; + vsync_worker_.VSyncControl(true); + + return HWC2::Error::None; } HWC2::Error HwcDisplay::SetAutoLowLatencyMode(bool /*on*/) { @@ -765,11 +781,18 @@ HWC2::Error HwcDisplay::SetContentType(int32_t contentType) { HWC2::Error HwcDisplay::GetDisplayIdentificationData(uint8_t *outPort, uint32_t *outDataSize, uint8_t *outData) { - auto blob = connector_->GetEdidBlob(); + if (IsInHeadlessMode()) { + return HWC2::Error::None; + } + auto blob = GetPipe().connector->Get()->GetEdidBlob(); + + *outPort = handle_ - 1; if (!blob) { - ALOGE("Failed to get edid property value."); - return HWC2::Error::Unsupported; + if (outData == nullptr) { + *outDataSize = 0; + } + return HWC2::Error::None; } if (outData) { @@ -778,7 +801,6 @@ HWC2::Error HwcDisplay::GetDisplayIdentificationData(uint8_t *outPort, } else { *outDataSize = blob->length; } - *outPort = connector_->id(); return HWC2::Error::None; } @@ -851,4 +873,37 @@ void HwcDisplay::set_backend(std::unique_ptr<Backend> backend) { backend_ = std::move(backend); } +/* returns true if composition should be sent to client */ +bool HwcDisplay::ProcessClientFlatteningState(bool skip) { + int flattenning_state = flattenning_state_; + if (flattenning_state == ClientFlattenningState::Disabled) { + return false; + } + + if (skip) { + flattenning_state_ = ClientFlattenningState::NotRequired; + return false; + } + + if (flattenning_state == ClientFlattenningState::ClientRefreshRequested) { + flattenning_state_ = ClientFlattenningState::Flattened; + return true; + } + + vsync_flattening_en_ = true; + vsync_worker_.VSyncControl(true); + flattenning_state_ = ClientFlattenningState::VsyncCountdownMax; + return false; +} + +void HwcDisplay::ProcessFlatenningVsyncInternal() { + if (flattenning_state_ > ClientFlattenningState::ClientRefreshRequested && + --flattenning_state_ == ClientFlattenningState::ClientRefreshRequested && + hwc2_->refresh_callback_.first != nullptr && + hwc2_->refresh_callback_.second != nullptr) { + hwc2_->refresh_callback_.first(hwc2_->refresh_callback_.second, handle_); + vsync_flattening_en_ = false; + } +} + } // namespace android |