diff --git a/1 b/1 new file mode 100644 index 00000000..9ffc8cbb --- /dev/null +++ b/1 @@ -0,0 +1,1121 @@ +/* + * Copyright (C) 2022 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 LOG_TAG "hwc-display" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "HwcDisplay.h" + +#include "DrmHwcTwo.h" +#include "backend/Backend.h" +#include "backend/BackendManager.h" +#include "bufferinfo/BufferInfoGetter.h" +#include "utils/log.h" +#include "utils/properties.h" +#include +#include + +namespace android { + +std::string HwcDisplay::DumpDelta(HwcDisplay::Stats delta) { + if (delta.total_pixops_ == 0) + return "No stats yet"; + double ratio = 1.0 - double(delta.gpu_pixops_) / double(delta.total_pixops_); + + std::stringstream ss; + ss << " Total frames count: " << delta.total_frames_ << "\n" + << " Failed to test commit frames: " << delta.failed_kms_validate_ << "\n" + << " Failed to commit frames: " << delta.failed_kms_present_ << "\n" + << ((delta.failed_kms_present_ > 0) + ? " !!! Internal failure, FIX it please\n" + : "") + << " Flattened frames: " << delta.frames_flattened_ << "\n" + << " Pixel operations (free units)" + << " : [TOTAL: " << delta.total_pixops_ << " / GPU: " << delta.gpu_pixops_ + << "]\n" + << " Composition efficiency: " << ratio; + + return ss.str(); +} + +std::string HwcDisplay::Dump() { + std::string flattening_state_str; + switch (flattenning_state_) { + case ClientFlattenningState::Disabled: + flattening_state_str = "Disabled"; + break; + case ClientFlattenningState::NotRequired: + flattening_state_str = "Not needed"; + break; + case ClientFlattenningState::Flattened: + flattening_state_str = "Active"; + break; + case ClientFlattenningState::ClientRefreshRequested: + flattening_state_str = "Refresh requested"; + break; + default: + flattening_state_str = std::to_string(flattenning_state_) + + " VSync remains"; + } + + std::string connector_name = IsInHeadlessMode() + ? "NULL-DISPLAY" + : GetPipe().connector->Get()->GetName(); + + std::stringstream ss; + ss << "- Display on: " << connector_name << "\n" + << " Flattening state: " << flattening_state_str << "\n" + << "Statistics since system boot:\n" + << DumpDelta(total_stats_) << "\n\n" + << "Statistics since last dumpsys request:\n" + << DumpDelta(total_stats_.minus(prev_stats_)) << "\n\n"; + + memcpy(&prev_stats_, &total_stats_, sizeof(Stats)); + return ss.str(); +} + +HwcDisplay::HwcDisplay(hwc2_display_t handle, HWC2::DisplayType type, + DrmHwcTwo *hwc2) + : hwc2_(hwc2), + handle_(handle), + type_(type), + client_layer_(this), + color_transform_hint_(HAL_COLOR_TRANSFORM_IDENTITY) { + // clang-format off + color_transform_matrix_ = {1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0}; + // clang-format on + if (type_ == HWC2::DisplayType::Virtual) { + writeback_layer_ = std::make_unique(this); + } +} + +HwcDisplay::~HwcDisplay() = default; + +void HwcDisplay::SetPipeline(DrmDisplayPipeline *pipeline) { + Deinit(); + + pipeline_ = pipeline; + + if (pipeline != nullptr || handle_ == kPrimaryDisplay) { + Init(); + hwc2_->ScheduleHotplugEvent(handle_, /*connected = */ true); + } else { + hwc2_->ScheduleHotplugEvent(handle_, /*connected = */ false); + } +} + +void HwcDisplay::Deinit() { + if (pipeline_ != nullptr) { + AtomicCommitArgs a_args{}; + a_args.active = false; + a_args.composition = std::make_shared(); + a_args.color_adjustment = GetPipe().device->GetColorAdjustmentEnabling(); + + GetPipe().atomic_state_manager->ExecuteAtomicCommit(a_args); + + vsync_worker_.Init(nullptr, [](int64_t) {}); + current_plan_.reset(); + backend_.reset(); + } + + SetClientTarget(nullptr, -1, 0, {}); +} + +HWC2::Error HwcDisplay::Init() { + ChosePreferredConfig(); + + int ret = vsync_worker_.Init(pipeline_, [this](int64_t timestamp) { + const std::lock_guard lock(hwc2_->GetResMan().GetMainLock()); + if (vsync_event_en_) { + uint32_t period_ns{}; + GetDisplayVsyncPeriod(&period_ns); + 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 && ret != -EALREADY) { + ALOGE("Failed to create event worker for d=%d %d\n", int(handle_), ret); + return HWC2::Error::BadDisplay; + } + + if (type_ != HWC2::DisplayType::Virtual) { + vsync_worker_ = VSyncWorker::CreateInstance(pipeline_, vsw_callbacks); + if (!vsync_worker_) { + ALOGE("Failed to create event worker for d=%d\n", int(handle_)); + 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 HWC2::Error::None; +} + +HWC2::Error HwcDisplay::ChosePreferredConfig() { + HWC2::Error err{}; + if (type_ == HWC2::DisplayType::Virtual) { + configs_.GenFakeMode(virtual_disp_width_, virtual_disp_height_); + } else + 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); +} + +HWC2::Error HwcDisplay::AcceptDisplayChanges() { + for (std::pair &l : layers_) + l.second.AcceptTypeChange(); + return HWC2::Error::None; +} + +HWC2::Error HwcDisplay::CreateLayer(hwc2_layer_t *layer) { + layers_.emplace(static_cast(layer_idx_), HwcLayer(this)); + *layer = static_cast(layer_idx_); + ++layer_idx_; + return HWC2::Error::None; +} + +HWC2::Error HwcDisplay::DestroyLayer(hwc2_layer_t 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(staged_mode_config_id_) == 0) + return HWC2::Error::BadConfig; + + *config = staged_mode_config_id_; + return HWC2::Error::None; +} + +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 &l : layers_) { + if (l.second.IsTypeChanged()) { + if (layers && num_changes < *num_elements) + layers[num_changes] = l.first; + if (types && num_changes < *num_elements) + types[num_changes] = static_cast(l.second.GetValidatedType()); + ++num_changes; + } + } + if (!layers && !types) + *num_elements = num_changes; + return HWC2::Error::None; +} + +HWC2::Error HwcDisplay::GetClientTargetSupport(uint32_t width, uint32_t height, + int32_t /*format*/, + int32_t dataspace) { + if (IsInHeadlessMode()) { + return HWC2::Error::None; + } + + std::pair min = pipeline_->device->GetMinResolution(); + std::pair max = pipeline_->device->GetMaxResolution(); + + if (width < min.first || height < min.second) + return HWC2::Error::Unsupported; + + if (width > max.first || height > max.second) + return HWC2::Error::Unsupported; + + if (dataspace != HAL_DATASPACE_UNKNOWN) + return HWC2::Error::Unsupported; + + // TODO(nobody): Validate format can be handled by either GL or planes + return HWC2::Error::None; +} + +HWC2::Error HwcDisplay::GetColorModes(uint32_t *num_modes, int32_t *modes) { + + if (IsInHeadlessMode()) { + if (num_modes) + *num_modes = 1; + + if (modes) + *modes = HAL_COLOR_MODE_NATIVE; + + return HWC2::Error::None; + } + + DrmConnector *conn = pipeline_->connector->Get(); + ALOGD("%s, num_modes:%p, modes:%p, conn:%p", __FUNCTION__, num_modes, modes, conn); + if (conn && (!conn->IsHdrSupportedDevice() || !conn->IsConnectorHdrCapable())) { + ALOGD("%s HDR mode is not supported!", __FUNCTION__); + if (!modes) { + if (num_modes) { + *num_modes = 1; + } else { + ALOGD("%s:%d num_modes is NULL!", __FUNCTION__, __LINE__); + } + } + + if (modes) + *modes = HAL_COLOR_MODE_NATIVE; + } + else { + ALOGD("%s HDR mode is supported.", __FUNCTION__); + if (!modes) { + if (num_modes) { + *num_modes = current_color_mode_.size(); + ALOGD("Set the num_modes to: %u!", (uint32_t) current_color_mode_.size()); + } else { + ALOGD("%s:%d num_modes is NULL!", __FUNCTION__, __LINE__); + } + } else { + if (num_modes) { + *num_modes = current_color_mode_.size(); + ALOGD("Set the num_modes to:%u", (uint32_t) current_color_mode_.size()); + } else { + ALOGD("%s:%d num_modes is null!", __FUNCTION__, __LINE__); + } + for (int i = 0; i < current_color_mode_.size(); i++) { + *(modes + i) = current_color_mode_[i]; + } + } + } + return HWC2::Error::None; +} + +HWC2::Error HwcDisplay::GetDisplayAttribute(hwc2_config_t config, + int32_t attribute_in, + int32_t *value) { + int conf = static_cast(config); + + if (configs_.hwc_configs.count(conf) == 0) { + ALOGE("Could not find mode #%d", conf); + return HWC2::Error::BadConfig; + } + + auto &hwc_config = configs_.hwc_configs[conf]; + + static const int32_t kUmPerInch = 25400; + uint32_t mm_width = configs_.mm_width; + uint32_t mm_height = configs_.mm_height; + auto attribute = static_cast(attribute_in); + switch (attribute) { + case HWC2::Attribute::Width: + *value = static_cast(hwc_config.mode.h_display()); + break; + case HWC2::Attribute::Height: + *value = static_cast(hwc_config.mode.v_display()); + break; + case HWC2::Attribute::VsyncPeriod: + // in nanoseconds + *value = static_cast(1E9 / hwc_config.mode.v_refresh()); + break; + case HWC2::Attribute::DpiX: + // Dots per 1000 inches + *value = mm_width ? static_cast(hwc_config.mode.h_display() * + kUmPerInch / mm_width) + : -1; + break; + case HWC2::Attribute::DpiY: + // Dots per 1000 inches + *value = mm_height ? static_cast(hwc_config.mode.v_display() * + kUmPerInch / mm_height) + : -1; + break; +#if PLATFORM_SDK_VERSION > 29 + 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 = int(hwc_config.group_id); + break; +#endif + default: + *value = -1; + return HWC2::Error::BadConfig; + } + return HWC2::Error::None; +} + +HWC2::Error HwcDisplay::GetDisplayConfigs(uint32_t *num_configs, + hwc2_config_t *configs) { + uint32_t idx = 0; + for (auto &hwc_config : configs_.hwc_configs) { + if (hwc_config.second.disabled) { + continue; + } + + if (configs != nullptr) { + if (idx >= *num_configs) { + break; + } + configs[idx] = hwc_config.second.id; + } + + idx++; + } + *num_configs = idx; + return HWC2::Error::None; +} + +HWC2::Error HwcDisplay::GetDisplayName(uint32_t *size, char *name) { + std::ostringstream stream; + if (IsInHeadlessMode()) { + stream << "null-display"; + } else { + stream << "display-" << GetPipe().connector->Get()->GetId(); + } + std::string string = stream.str(); + size_t length = string.length(); + if (!name) { + *size = length; + return HWC2::Error::None; + } + + *size = std::min(static_cast(length - 1), *size); + strncpy(name, string.c_str(), *size); + return HWC2::Error::None; +} + +HWC2::Error HwcDisplay::GetDisplayRequests(int32_t * /*display_requests*/, + uint32_t *num_elements, + hwc2_layer_t * /*layers*/, + int32_t * /*layer_requests*/) { + // TODO(nobody): I think virtual display should request + // HWC2_DISPLAY_REQUEST_WRITE_CLIENT_TARGET_TO_OUTPUT here + *num_elements = 0; + return HWC2::Error::None; +} + +HWC2::Error HwcDisplay::GetDisplayType(int32_t *type) { + *type = static_cast(type_); + return HWC2::Error::None; +} + +HWC2::Error HwcDisplay::GetDozeSupport(int32_t *support) { + *support = 0; + return HWC2::Error::None; +} + +HWC2::Error HwcDisplay::GetPerFrameMetadataKeys(uint32_t *outNumKeys, int32_t *outKeys) { + if (NULL == outNumKeys) { + return HWC2::Error::BadParameter; + } + + *outNumKeys = KEY_NUM_PER_FRAME_METADATA_KEYS; + if (NULL == outKeys) + return HWC2::Error::None; + + for (int i = 0; i < KEY_NUM_PER_FRAME_METADATA_KEYS; i++) { + *(outKeys + i) = i; + } + + return HWC2::Error::None; +} + +HWC2::Error HwcDisplay::GetHdrCapabilities(uint32_t *num_types, + int32_t * types, + float * max_luminance, + float * max_average_luminance, + float * min_luminance) { + *num_types = 0; + if (IsInHeadlessMode()) { + return HWC2::Error::Unsupported; + } + + DrmConnector *conn = pipeline_->connector->Get(); + + if (conn && conn->IsHdrSupportedDevice()) { + if (conn->GetHdrCapabilities(num_types, types, max_luminance, + max_average_luminance, min_luminance)) { + return HWC2::Error::None; + } else { + return HWC2::Error::Unsupported; + } + } + + + return HWC2::Error::None; +} + +/* Find API details at: + * https://cs.android.com/android/platform/superproject/+/android-11.0.0_r3:hardware/libhardware/include/hardware/hwcomposer2.h;l=1767 + * + * Called after PresentDisplay(), CLIENT is expecting release fence for the + * prior buffer (not the one assigned to the layer at the moment). + */ +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 (auto &l : layers_) { + if (!l.second.GetPriorBufferScanOutFlag() || !present_fence_) { + continue; + } + + ++num_layers; + + if (layers == nullptr || fences == nullptr) + continue; + + if (num_layers > *num_elements) { + ALOGW("Overflow num_elements %d/%d", num_layers, *num_elements); + return HWC2::Error::None; + } + + layers[num_layers - 1] = l.first; + fences[num_layers - 1] = UniqueFd::Dup(present_fence_.Get()).Release(); + } + *num_elements = num_layers; + + return HWC2::Error::None; +} + +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; + } + + int PrevModeVsyncPeriodNs = static_cast( + 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(staged_mode_->h_display()), + .bottom = static_cast(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; + } + } + + a_args.color_adjustment = GetPipe().device->GetColorAdjustmentEnabling(); + + // order the layers by z-order + bool use_client_layer = false; + uint32_t client_z_order = UINT32_MAX; + std::map z_map; + for (std::pair &l : layers_) { + switch (l.second.GetValidatedType()) { + case HWC2::Composition::Device: + z_map.emplace(std::make_pair(l.second.GetZOrder(), &l.second)); + break; + case HWC2::Composition::Client: + // Place it at the z_order of the lowest client layer + use_client_layer = true; + client_z_order = std::min(client_z_order, l.second.GetZOrder()); + break; + default: + continue; + } + } + if (use_client_layer) + z_map.emplace(std::make_pair(client_z_order, &client_layer_)); + + if (z_map.empty()) + return HWC2::Error::BadLayer; + + std::vector composition_layers; + + /* Import & populate */ + for (std::pair &l : z_map) { + l.second->PopulateLayerData(a_args.test_only); + } + + // now that they're ordered by z, add them to the composition + for (std::pair &l : z_map) { + if (!l.second->IsLayerUsableAsDevice()) { + /* This will be normally triggered on validation of the first frame + * containing CLIENT layer. At this moment client buffer is not yet + * provided by the CLIENT. + * This may be triggered once in HwcLayer lifecycle in case FB can't be + * imported. For example when non-contiguous buffer is imported into + * contiguous-only DRM/KMS driver. + */ + return HWC2::Error::BadLayer; + } + composition_layers.emplace_back(l.second->GetLayerData().Clone()); + } + + /* 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 (type_ == HWC2::DisplayType::Virtual) { + a_args.writeback_fb = writeback_layer_->GetLayerData().fb; + a_args.writeback_release_fence = writeback_layer_->GetLayerData() + .acquire_fence; + } + + if (!current_plan_) { + if (!a_args.test_only) { + ALOGE("Failed to create DrmKmsPlan"); + } + return HWC2::Error::BadConfig; + } + + a_args.composition = current_plan_; + + int ret = GetPipe().atomic_state_manager->ExecuteAtomicCommit(a_args); + + if (ret) { + if (!a_args.test_only) + ALOGE("Failed to apply the frame composition ret=%d", ret); + return HWC2::Error::BadParameter; + } + + 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; +} + +/* Find API details at: + * 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 *out_present_fence) { + if (expectedPresentTime_.has_value() && expectedPresentTime_->timestampNanos > 0) { + static const int64_t kOneSecondNs = 1LL * 1000 * 1000 * 1000; + struct timespec vsync {}; + clock_gettime(CLOCK_MONOTONIC, &vsync); + int64_t timestamp = (int64_t)vsync.tv_sec * kOneSecondNs + (int64_t)vsync.tv_nsec; + int64_t half_period = (1E9 / staged_mode_->v_refresh()) / 2; + if ((expectedPresentTime_->timestampNanos - timestamp) > half_period) { + int64_t sleep_ms = (expectedPresentTime_->timestampNanos - timestamp - half_period) / (1000 * 1000); + std::this_thread::sleep_for(std::chrono::milliseconds(sleep_ms)); + } + expectedPresentTime_ = std::nullopt; + } else { + std::this_thread::sleep_for(std::chrono::nanoseconds((int64_t)(1E9 / staged_mode_->v_refresh()) / 2)); + } + if (IsInHeadlessMode()) { + *out_present_fence = -1; + return HWC2::Error::None; + } + HWC2::Error ret{}; + + ++total_stats_.total_frames_; + + AtomicCommitArgs a_args{}; + ret = CreateComposition(a_args); + + if (ret != HWC2::Error::None) + ++total_stats_.failed_kms_present_; + + if (ret == HWC2::Error::BadLayer) { + // Can we really have no client or device layers? + *out_present_fence = -1; + return HWC2::Error::None; + } + if (ret != HWC2::Error::None) + return ret; + + this->present_fence_ = UniqueFd::Dup(a_args.out_fence.Get()); + *out_present_fence = a_args.out_fence.Release(); + + ++frame_no_; + return HWC2::Error::None; +} + +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; + } + + 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 + */ +HWC2::Error HwcDisplay::SetClientTarget(buffer_handle_t target, + int32_t acquire_fence, + int32_t dataspace, + hwc_region_t /*damage*/) { + client_layer_.SetLayerBuffer(target, acquire_fence); + client_layer_.SetLayerDataspace(dataspace); + + /* + * target can be nullptr, this does mean the Composer Service is calling + * cleanDisplayResources() on after receiving HOTPLUG event. See more at: + * https://cs.android.com/android/platform/superproject/+/master:hardware/interfaces/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerClient.h;l=350;drc=944b68180b008456ed2eb4d4d329e33b19bd5166 + */ + if (target == nullptr) { + client_layer_.SwChainClearCache(); + return HWC2::Error::None; + } + + if (IsInHeadlessMode()) { + return HWC2::Error::None; + } + + client_layer_.PopulateLayerData(/*test = */ true); + if (!client_layer_.IsLayerUsableAsDevice()) { + ALOGE("Client layer must be always usable by DRM/KMS"); + return HWC2::Error::BadLayer; + } + + auto &bi = client_layer_.GetLayerData().bi; + hwc_frect_t source_crop = {.left = 0.0F, + .top = 0.0F, + .right = static_cast(bi->width), + .bottom = static_cast(bi->height)}; + client_layer_.SetLayerSourceCrop(source_crop); + + return HWC2::Error::None; +} + +HWC2::Error HwcDisplay::SetColorMode(int32_t mode) { + if (mode < HAL_COLOR_MODE_NATIVE || mode > HAL_COLOR_MODE_BT2100_HLG) + return HWC2::Error::BadParameter; + + if (mode != HAL_COLOR_MODE_NATIVE) + return HWC2::Error::Unsupported; + + color_mode_ = mode; + return HWC2::Error::None; +} + +HWC2::Error HwcDisplay::SetColorTransform(const float *matrix, int32_t hint) { + if (hint < HAL_COLOR_TRANSFORM_IDENTITY || + hint > HAL_COLOR_TRANSFORM_CORRECT_TRITANOPIA) + return HWC2::Error::BadParameter; + + if (!matrix && hint == HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX) + return HWC2::Error::BadParameter; + + color_transform_hint_ = static_cast(hint); + if (color_transform_hint_ == HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX) + std::copy(matrix, matrix + MATRIX_SIZE, color_transform_matrix_.begin()); + + return HWC2::Error::None; +} + +HWC2::Error HwcDisplay::SetOutputBuffer(buffer_handle_t buffer, + int32_t release_fence) { + writeback_layer_->SetLayerBuffer(buffer, release_fence); + writeback_layer_->PopulateLayerData(); + if (!writeback_layer_->IsLayerUsableAsDevice()) { + ALOGE("Output layer must be always usable by DRM/KMS"); + return HWC2::Error::BadLayer; + } + /* TODO: Check if format is supported by writeback connector */ + return HWC2::Error::None; +} + +HWC2::Error HwcDisplay::SetPowerMode(int32_t mode_in) { + auto mode = static_cast(mode_in); + + AtomicCommitArgs a_args{}; + + switch (mode) { + case HWC2::PowerMode::Off: + a_args.active = false; + break; + case HWC2::PowerMode::On: + a_args.active = true; + a_args.color_adjustment = GetPipe().device->GetColorAdjustmentEnabling(); + break; + case HWC2::PowerMode::Doze: + case HWC2::PowerMode::DozeSuspend: + return HWC2::Error::Unsupported; + default: + ALOGE("Incorrect power mode value (%d)\n", mode); + return HWC2::Error::BadParameter; + } + + if (IsInHeadlessMode()) { + return HWC2::Error::None; + } + + if (a_args.active) { + /* + * Setting the display to active before we have a composition + * can break some drivers, so skip setting a_args.active to + * true, as the next composition frame will implicitly activate + * the display + */ + return GetPipe().atomic_state_manager->ActivateDisplayUsingDPMS() == 0 + ? HWC2::Error::None + : HWC2::Error::BadParameter; + }; + + 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; + } + return HWC2::Error::None; +} + +HWC2::Error HwcDisplay::SetVsyncEnabled(int32_t enabled) { + if (type_ == HWC2::DisplayType::Virtual) { + return HWC2::Error::None; + } + + vsync_event_en_ = HWC2_VSYNC_ENABLE == enabled; + if (vsync_event_en_) { + vsync_worker_.VSyncControl(true); + } + return HWC2::Error::None; +} + +HWC3::Error HwcDisplay::setExpectedPresentTime( + const std::optional& expectedPresentTime) { + if (expectedPresentTime.has_value()) + expectedPresentTime_ = expectedPresentTime; + return HWC3::Error::None; +} + +HWC2::Error HwcDisplay::ValidateDisplay(uint32_t *num_types, + uint32_t *num_requests) { + if (IsInHeadlessMode()) { + *num_types = *num_requests = 0; + return HWC2::Error::None; + } + + /* In current drm_hwc design in case previous frame layer was not validated as + * a CLIENT, it is used by display controller (Front buffer). We have to store + * this state to provide the CLIENT with the release fences for such buffers. + */ + for (auto &l : layers_) { + l.second.SetPriorBufferScanOutFlag(l.second.GetValidatedType() != + HWC2::Composition::Client); + } + + return backend_->ValidateDisplay(this, num_types, num_requests); +} + +std::vector HwcDisplay::GetOrderLayersByZPos() { + std::vector ordered_layers; + ordered_layers.reserve(layers_.size()); + + for (auto &[handle, layer] : layers_) { + ordered_layers.emplace_back(&layer); + } + + std::sort(std::begin(ordered_layers), std::end(ordered_layers), + [](const HwcLayer *lhs, const HwcLayer *rhs) { + return lhs->GetZOrder() < rhs->GetZOrder(); + }); + + 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 (IsInHeadlessMode()) { + *outType = static_cast(HWC2::DisplayConnectionType::Internal); + 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(HWC2::DisplayConnectionType::Internal); + else if (GetPipe().connector->Get()->IsExternal()) + *outType = static_cast(HWC2::DisplayConnectionType::External); + else + return HWC2::Error::BadConfig; + + return HWC2::Error::None; +} + +HWC2::Error HwcDisplay::SetActiveConfigWithConstraints( + hwc2_config_t config, + hwc_vsync_period_change_constraints_t *vsyncPeriodChangeConstraints, + hwc_vsync_period_change_timeline_t *outTimeline) { + if (type_ == HWC2::DisplayType::Virtual) { + return HWC2::Error::None; + } + + if (vsyncPeriodChangeConstraints == nullptr || outTimeline == nullptr) { + return HWC2::Error::BadParameter; + } + + 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*/) { + return HWC2::Error::Unsupported; +} + +HWC2::Error HwcDisplay::GetSupportedContentTypes( + uint32_t *outNumSupportedContentTypes, + const uint32_t *outSupportedContentTypes) { + if (outSupportedContentTypes == nullptr) + *outNumSupportedContentTypes = 0; + + return HWC2::Error::None; +} + +HWC2::Error HwcDisplay::SetContentType(int32_t contentType) { + if (contentType != HWC2_CONTENT_TYPE_NONE) + return HWC2::Error::Unsupported; + + /* TODO: Map to the DRM Connector property: + * https://elixir.bootlin.com/linux/v5.4-rc5/source/drivers/gpu/drm/drm_connector.c#L809 + */ + + return HWC2::Error::None; +} +#endif + +#if PLATFORM_SDK_VERSION > 28 +HWC2::Error HwcDisplay::GetDisplayIdentificationData(uint8_t *outPort, + uint32_t *outDataSize, + uint8_t *outData) { + if (IsInHeadlessMode()) { + return HWC2::Error::Unsupported; + } + + auto blob = GetPipe().connector->Get()->GetEdidBlob(); + if (!blob) { + return HWC2::Error::Unsupported; + } + + *outPort = handle_; /* TDOD(nobody): What should be here? */ + + if (outData) { + *outDataSize = std::min(*outDataSize, blob->length); + memcpy(outData, blob->data, *outDataSize); + } else { + *outDataSize = blob->length; + } + + return HWC2::Error::None; +} + +HWC2::Error HwcDisplay::GetDisplayCapabilities(uint32_t *outNumCapabilities, + uint32_t * /*outCapabilities*/) { + if (outNumCapabilities == nullptr) { + return HWC2::Error::BadParameter; + } + + *outNumCapabilities = 0; + + return HWC2::Error::None; +} + +HWC2::Error HwcDisplay::GetDisplayBrightnessSupport(bool *supported) { + *supported = false; + return HWC2::Error::None; +} + +HWC2::Error HwcDisplay::SetDisplayBrightness(float /* brightness */) { + return HWC2::Error::Unsupported; +} + +#endif /* PLATFORM_SDK_VERSION > 28 */ + +#if PLATFORM_SDK_VERSION > 27 + +HWC2::Error HwcDisplay::GetRenderIntents( + int32_t mode, uint32_t *outNumIntents, + int32_t * /*android_render_intent_v1_1_t*/ outIntents) { + + if (NULL == outNumIntents || mode < HAL_COLOR_MODE_NATIVE || mode > HAL_COLOR_MODE_DISPLAY_P3) { + return HWC2::Error::BadParameter; + } + + if (IsInHeadlessMode()) { + *outNumIntents = 1; + if (outIntents) + outIntents[0] = HAL_RENDER_INTENT_COLORIMETRIC; + return HWC2::Error::None; + } + + DrmConnector *conn = pipeline_->connector->Get(); + + if (conn && !conn->IsHdrSupportedDevice()) { + if (mode != HAL_COLOR_MODE_NATIVE) { + return HWC2::Error::BadParameter; + } + + if (outIntents == nullptr) { + *outNumIntents = 1; + return HWC2::Error::None; + } + *outNumIntents = 1; + outIntents[0] = HAL_RENDER_INTENT_COLORIMETRIC; + } else { + // Add the SDR render intents by default. + if (NULL == outIntents) { + *outNumIntents = 2; + return HWC2::Error::None; + } + + *(outIntents) = HAL_RENDER_INTENT_COLORIMETRIC; + *(outIntents + 1) = HAL_RENDER_INTENT_ENHANCE; + } + + return HWC2::Error::None; +} + +HWC2::Error HwcDisplay::SetColorModeWithIntent(int32_t mode, int32_t intent) { + if (intent < HAL_RENDER_INTENT_COLORIMETRIC || + intent > HAL_RENDER_INTENT_TONE_MAP_ENHANCE) + return HWC2::Error::BadParameter; + + if (mode < HAL_COLOR_MODE_NATIVE || mode > HAL_COLOR_MODE_BT2100_HLG) + return HWC2::Error::BadParameter; + + if (mode != HAL_COLOR_MODE_NATIVE) + return HWC2::Error::Unsupported; + + if (intent != HAL_RENDER_INTENT_COLORIMETRIC) + return HWC2::Error::Unsupported; + + color_mode_ = mode; + return HWC2::Error::None; +} + +#endif /* PLATFORM_SDK_VERSION > 27 */ + +const Backend *HwcDisplay::backend() const { + return backend_.get(); +} + +void HwcDisplay::set_backend(std::unique_ptr 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 diff --git a/drm/DrmAtomicStateManager.cpp b/drm/DrmAtomicStateManager.cpp index 7e7870c0..1d6aa464 100644 --- a/drm/DrmAtomicStateManager.cpp +++ b/drm/DrmAtomicStateManager.cpp @@ -74,8 +74,26 @@ auto DrmAtomicStateManager::CommitFrame(AtomicCommitArgs &args) -> int { } int out_fence = -1; - if (!crtc->GetOutFencePtrProperty().AtomicSet(*pset, uint64_t(&out_fence))) { - return -EINVAL; + if (!args.writeback_fb) { + if (!crtc->GetOutFencePtrProperty(). // + AtomicSet(*pset, uint64_t(&out_fence))) { + return -EINVAL; + } + } else { + if (!connector->GetWritebackOutFenceProperty(). // + AtomicSet(*pset, uint64_t(&out_fence))) { + return -EINVAL; + } + + if (!connector->GetWritebackFbIdProperty(). // + AtomicSet(*pset, args.writeback_fb->GetFbId())) { + return -EINVAL; + } + + if (args.writeback_release_fence) { + sync_wait(*args.writeback_release_fence, -1); + args.writeback_release_fence.reset(); + } } bool nonblock = true; diff --git a/drm/DrmAtomicStateManager.h b/drm/DrmAtomicStateManager.h index 2661df21..11afd542 100644 --- a/drm/DrmAtomicStateManager.h +++ b/drm/DrmAtomicStateManager.h @@ -41,6 +41,8 @@ struct AtomicCommitArgs { std::optional active; std::shared_ptr composition; bool color_adjustment = false; + std::shared_ptr writeback_fb; + SharedFd writeback_release_fence; /* out */ UniqueFd out_fence; diff --git a/drm/DrmConnector.h b/drm/DrmConnector.h index 456a879a..a2839d3b 100644 --- a/drm/DrmConnector.h +++ b/drm/DrmConnector.h @@ -114,6 +114,13 @@ class DrmConnector : public PipelineBindable { return hdr_metadata_; } + auto &GetWritebackFbIdProperty() const { + return writeback_fb_id_; + } + + auto &GetWritebackOutFenceProperty() const { + return writeback_out_fence_; + } auto IsConnected() const { return connector_->connection == DRM_MODE_CONNECTED; diff --git a/drm/DrmDevice.cpp b/drm/DrmDevice.cpp index 5da2c2de..cedba081 100644 --- a/drm/DrmDevice.cpp +++ b/drm/DrmDevice.cpp @@ -317,6 +317,11 @@ auto DrmDevice::GetConnectors() return connectors_; } +auto DrmDevice::GetWritebackConnectors() + -> const std::vector> & { + return writeback_connectors_; +} + auto DrmDevice::GetPlanes() -> const std::vector> & { return planes_; } diff --git a/drm/DrmDevice.h b/drm/DrmDevice.h index 61bc0e2a..f2f9c6f2 100644 --- a/drm/DrmDevice.h +++ b/drm/DrmDevice.h @@ -50,6 +50,7 @@ class DrmDevice { } auto GetConnectors() -> const std::vector> &; + auto GetWritebackConnectors() -> const std::vector> &; auto GetPlanes() -> const std::vector> &; auto GetCrtcs() -> const std::vector> &; auto GetEncoders() -> const std::vector> &; @@ -121,7 +122,7 @@ class DrmDevice { bool hdr_device_checked_ = false; std::vector> connectors_; - std::vector> writeback_connectors_; + std::vector> rriteback_connectors_; std::vector> encoders_; std::vector> crtcs_; std::vector> planes_; diff --git a/drm/ResourceManager.cpp b/drm/ResourceManager.cpp index 40bbd843..f646e692 100644 --- a/drm/ResourceManager.cpp +++ b/drm/ResourceManager.cpp @@ -293,4 +293,30 @@ auto ResourceManager::GetOrderedConnectors() -> std::vector { return ordered_connectors; } + +auto ResourceManager::GetVirtualDisplayPipeline() + -> std::shared_ptr { + for (auto &drm : drms_) { + for (const auto &conn : drm->GetWritebackConnectors()) { + auto pipeline = DrmDisplayPipeline::CreatePipeline(*conn); + if (!pipeline) { + ALOGE("Failed to create pipeline for writeback connector %s", + conn->GetName().c_str()); + } + if (pipeline) { + return pipeline; + } + } + } + return {}; +} + +auto ResourceManager::GetWritebackConnectorsCount() -> uint32_t { + uint32_t count = 0; + for (auto &drm : drms_) { + count += drm->GetWritebackConnectors().size(); + } + return count; +} + } // namespace android diff --git a/drm/ResourceManager.h b/drm/ResourceManager.h index c4450116..967b9b07 100644 --- a/drm/ResourceManager.h +++ b/drm/ResourceManager.h @@ -57,6 +57,8 @@ class ResourceManager { return main_lock_; } + auto GetVirtualDisplayPipeline() -> std::shared_ptr; + auto GetWritebackConnectorsCount() -> uint32_t; static auto GetTimeMonotonicNs() -> int64_t; private: diff --git a/hwc2_device/HwcDisplay.cpp b/hwc2_device/HwcDisplay.cpp index 6a250e85..a978f64d 100644 --- a/hwc2_device/HwcDisplay.cpp +++ b/hwc2_device/HwcDisplay.cpp @@ -100,6 +100,9 @@ HwcDisplay::HwcDisplay(hwc2_display_t handle, HWC2::DisplayType type, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0}; // clang-format on + if (type_ == HWC2::DisplayType::Virtual) { + writeback_layer_ = std::make_unique(this); + } } HwcDisplay::~HwcDisplay() = default; @@ -159,6 +162,14 @@ HWC2::Error HwcDisplay::Init() { return HWC2::Error::BadDisplay; } + if (type_ != HWC2::DisplayType::Virtual) { + vsync_worker_ = VSyncWorker::CreateInstance(pipeline_, vsw_callbacks); + if (!vsync_worker_) { + ALOGE("Failed to create event worker for d=%d\n", int(handle_)); + return HWC2::Error::BadDisplay; + } + } + if (!IsInHeadlessMode()) { ret = BackendManager::GetInstance().SetBackendForDisplay(this); if (ret) { @@ -174,6 +185,9 @@ HWC2::Error HwcDisplay::Init() { HWC2::Error HwcDisplay::ChosePreferredConfig() { HWC2::Error err{}; + if (type_ == HWC2::DisplayType::Virtual) { + configs_.GenFakeMode(virtual_disp_width_, virtual_disp_height_); + } else if (!IsInHeadlessMode()) { err = configs_.Update(*pipeline_->connector->Get()); } else { @@ -585,6 +599,13 @@ HWC2::Error HwcDisplay::CreateComposition(AtomicCommitArgs &a_args) { */ current_plan_ = DrmKmsPlan::CreateDrmKmsPlan(GetPipe(), std::move(composition_layers)); + + if (type_ == HWC2::DisplayType::Virtual) { + a_args.writeback_fb = writeback_layer_->GetLayerData().fb; + a_args.writeback_release_fence = writeback_layer_->GetLayerData() + .acquire_fence; + } + if (!current_plan_) { if (!a_args.test_only) { ALOGE("Failed to create DrmKmsPlan"); @@ -745,10 +766,16 @@ HWC2::Error HwcDisplay::SetColorTransform(const float *matrix, int32_t hint) { return HWC2::Error::None; } -HWC2::Error HwcDisplay::SetOutputBuffer(buffer_handle_t /*buffer*/, - int32_t /*release_fence*/) { - // TODO(nobody): Need virtual display support - return HWC2::Error::Unsupported; +HWC2::Error HwcDisplay::SetOutputBuffer(buffer_handle_t buffer, + int32_t release_fence) { + writeback_layer_->SetLayerBuffer(buffer, release_fence); + writeback_layer_->PopulateLayerData(); + if (!writeback_layer_->IsLayerUsableAsDevice()) { + ALOGE("Output layer must be always usable by DRM/KMS"); + return HWC2::Error::BadLayer; + } + /* TODO: Check if format is supported by writeback connector */ + return HWC2::Error::None; } HWC2::Error HwcDisplay::SetPowerMode(int32_t mode_in) { @@ -797,6 +824,10 @@ HWC2::Error HwcDisplay::SetPowerMode(int32_t mode_in) { } HWC2::Error HwcDisplay::SetVsyncEnabled(int32_t enabled) { + if (type_ == HWC2::DisplayType::Virtual) { + return HWC2::Error::None; + } + vsync_event_en_ = HWC2_VSYNC_ENABLE == enabled; if (vsync_event_en_) { vsync_worker_.VSyncControl(true); @@ -876,6 +907,10 @@ HWC2::Error HwcDisplay::SetActiveConfigWithConstraints( hwc2_config_t config, hwc_vsync_period_change_constraints_t *vsyncPeriodChangeConstraints, hwc_vsync_period_change_timeline_t *outTimeline) { + if (type_ == HWC2::DisplayType::Virtual) { + return HWC2::Error::None; + } + if (vsyncPeriodChangeConstraints == nullptr || outTimeline == nullptr) { return HWC2::Error::BadParameter; } diff --git a/hwc2_device/HwcDisplay.h b/hwc2_device/HwcDisplay.h index 38ee6d96..47fbd096 100644 --- a/hwc2_device/HwcDisplay.h +++ b/hwc2_device/HwcDisplay.h @@ -187,6 +187,15 @@ class HwcDisplay { void Deinit(); + auto &GetWritebackLayer() { + return writeback_layer_; + } + + void SetVirtualDisplayResolution(uint16_t width, uint16_t height) { + virtual_disp_width_ = width; + virtual_disp_height_ = height; + } + private: enum ClientFlattenningState : int32_t { Disabled = -3, @@ -227,6 +236,9 @@ class HwcDisplay { std::map layers_; HwcLayer client_layer_; + std::unique_ptr writeback_layer_; + uint16_t virtual_disp_width_{}; + uint16_t virtual_disp_height_{}; int32_t color_mode_{}; std::vector current_color_mode_ = {HAL_COLOR_MODE_NATIVE, HAL_COLOR_MODE_BT2020, HAL_COLOR_MODE_BT2100_PQ, HAL_COLOR_MODE_BT2100_HLG, /*HAL_COLOR_MODE_DISPLAY_BT2020*/}; std::array color_transform_matrix_{};