| /* | |
| * Copyright (C) 2010 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. | |
| */ | |
| // TODO(b/129481165): remove the #pragma below and fix conversion issues | |
| #include <chrono> | |
| #pragma clang diagnostic push | |
| #pragma clang diagnostic ignored "-Wconversion" | |
| // #define LOG_NDEBUG 0 | |
| #undef LOG_TAG | |
| #define LOG_TAG "HWComposer" | |
| #define ATRACE_TAG ATRACE_TAG_GRAPHICS | |
| #include "HWComposer.h" | |
| #include <aidl/android/hardware/graphics/composer3/IComposerClient.h> | |
| #include <android-base/properties.h> | |
| #include <common/trace.h> | |
| #include <compositionengine/Output.h> | |
| #include <compositionengine/OutputLayer.h> | |
| #include <compositionengine/impl/OutputLayerCompositionState.h> | |
| #include <ftl/concat.h> | |
| #include <log/log.h> | |
| #include <ui/DebugUtils.h> | |
| #include <ui/GraphicBuffer.h> | |
| #include <utils/Errors.h> | |
| #include "../Layer.h" // needed only for debugging | |
| #include "../SurfaceFlingerProperties.h" | |
| #include "ComposerHal.h" | |
| #include "HWC2.h" | |
| #define LOG_HWC_DISPLAY_ERROR(hwcDisplayId, msg) \ | |
| ALOGE("%s failed for HWC display %" PRIu64 ": %s", __FUNCTION__, hwcDisplayId, msg) | |
| #define LOG_DISPLAY_ERROR(displayId, msg) \ | |
| ALOGE("%s failed for display %s: %s", __FUNCTION__, to_string(displayId).c_str(), msg) | |
| #define LOG_HWC_ERROR(what, error, displayId) \ | |
| ALOGE("%s: %s failed for display %s: %s (%d)", __FUNCTION__, what, \ | |
| to_string(displayId).c_str(), to_string(error).c_str(), static_cast<int32_t>(error)) | |
| #define RETURN_IF_INVALID_DISPLAY(displayId, ...) \ | |
| do { \ | |
| if (mDisplayData.count(displayId) == 0) { \ | |
| LOG_DISPLAY_ERROR(displayId, "Invalid display"); \ | |
| return __VA_ARGS__; \ | |
| } \ | |
| } while (false) | |
| #define RETURN_IF_HWC_ERROR_FOR(what, error, displayId, ...) \ | |
| do { \ | |
| if (error != hal::Error::NONE) { \ | |
| LOG_HWC_ERROR(what, error, displayId); \ | |
| return __VA_ARGS__; \ | |
| } \ | |
| } while (false) | |
| #define RETURN_IF_HWC_ERROR(error, displayId, ...) \ | |
| RETURN_IF_HWC_ERROR_FOR(__FUNCTION__, error, displayId, __VA_ARGS__) | |
| using aidl::android::hardware::graphics::common::HdrConversionCapability; | |
| using aidl::android::hardware::graphics::common::HdrConversionStrategy; | |
| using aidl::android::hardware::graphics::composer3::Capability; | |
| using aidl::android::hardware::graphics::composer3::DisplayCapability; | |
| using aidl::android::hardware::graphics::composer3::DisplayConfiguration; | |
| using namespace std::string_literals; | |
| namespace android { | |
| HWComposer::~HWComposer() = default; | |
| namespace impl { | |
| HWComposer::HWComposer(std::unique_ptr<Hwc2::Composer> composer) | |
| : mComposer(std::move(composer)), | |
| mMaxVirtualDisplayDimension(static_cast<size_t>(sysprop::max_virtual_display_dimension(0))), | |
| mUpdateDeviceProductInfoOnHotplugReconnect( | |
| sysprop::update_device_product_info_on_hotplug_reconnect(false)), | |
| mEnableVrrTimeout(base::GetBoolProperty("debug.sf.vrr_timeout_hint_enabled"s, true)) {} | |
| HWComposer::HWComposer(const std::string& composerServiceName) | |
| : HWComposer(Hwc2::Composer::create(composerServiceName)) {} | |
| HWComposer::~HWComposer() { | |
| mDisplayData.clear(); | |
| } | |
| void HWComposer::setCallback(HWC2::ComposerCallback& callback) { | |
| loadCapabilities(); | |
| loadLayerMetadataSupport(); | |
| loadOverlayProperties(); | |
| loadHdrConversionCapabilities(); | |
| if (mRegisteredCallback) { | |
| ALOGW("Callback already registered. Ignored extra registration attempt."); | |
| return; | |
| } | |
| mRegisteredCallback = true; | |
| mComposer->registerCallback(callback); | |
| } | |
| bool HWComposer::getDisplayIdentificationData(hal::HWDisplayId hwcDisplayId, uint8_t* outPort, | |
| DisplayIdentificationData* outData) const { | |
| const auto error = static_cast<hal::Error>( | |
| mComposer->getDisplayIdentificationData(hwcDisplayId, outPort, outData)); | |
| if (error != hal::Error::NONE) { | |
| if (error != hal::Error::UNSUPPORTED) { | |
| LOG_HWC_DISPLAY_ERROR(hwcDisplayId, to_string(error).c_str()); | |
| } | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool HWComposer::hasCapability(Capability capability) const { | |
| return mCapabilities.count(capability) > 0; | |
| } | |
| bool HWComposer::hasDisplayCapability(HalDisplayId displayId, DisplayCapability capability) const { | |
| RETURN_IF_INVALID_DISPLAY(displayId, false); | |
| return mDisplayData.at(displayId).hwcDisplay->hasCapability(capability); | |
| } | |
| std::optional<DisplayIdentificationInfo> HWComposer::onHotplug(hal::HWDisplayId hwcDisplayId, | |
| HotplugEvent event) { | |
| switch (event) { | |
| case HotplugEvent::Connected: | |
| return onHotplugConnect(hwcDisplayId); | |
| case HotplugEvent::Disconnected: | |
| return onHotplugDisconnect(hwcDisplayId); | |
| case HotplugEvent::LinkUnstable: | |
| return onHotplugLinkTrainingFailure(hwcDisplayId); | |
| } | |
| } | |
| bool HWComposer::updatesDeviceProductInfoOnHotplugReconnect() const { | |
| return mUpdateDeviceProductInfoOnHotplugReconnect; | |
| } | |
| std::optional<PhysicalDisplayId> HWComposer::onVsync(hal::HWDisplayId hwcDisplayId, | |
| nsecs_t timestamp) { | |
| const auto displayIdOpt = toPhysicalDisplayId(hwcDisplayId); | |
| if (!displayIdOpt) { | |
| LOG_HWC_DISPLAY_ERROR(hwcDisplayId, "Invalid HWC display"); | |
| return {}; | |
| } | |
| RETURN_IF_INVALID_DISPLAY(*displayIdOpt, {}); | |
| auto& displayData = mDisplayData[*displayIdOpt]; | |
| { | |
| // There have been reports of HWCs that signal several vsync events | |
| // with the same timestamp when turning the display off and on. This | |
| // is a bug in the HWC implementation, but filter the extra events | |
| // out here so they don't cause havoc downstream. | |
| if (timestamp == displayData.lastPresentTimestamp) { | |
| ALOGW("Ignoring duplicate VSYNC event from HWC for display %s (t=%" PRId64 ")", | |
| to_string(*displayIdOpt).c_str(), timestamp); | |
| return {}; | |
| } | |
| displayData.lastPresentTimestamp = timestamp; | |
| } | |
| SFTRACE_INT(ftl::Concat("HW_VSYNC_", displayIdOpt->value).c_str(), | |
| displayData.vsyncTraceToggle); | |
| displayData.vsyncTraceToggle = !displayData.vsyncTraceToggle; | |
| return displayIdOpt; | |
| } | |
| size_t HWComposer::getMaxVirtualDisplayCount() const { | |
| return mComposer->getMaxVirtualDisplayCount(); | |
| } | |
| size_t HWComposer::getMaxVirtualDisplayDimension() const { | |
| return mMaxVirtualDisplayDimension; | |
| } | |
| bool HWComposer::allocateVirtualDisplay(HalVirtualDisplayId displayId, ui::Size resolution, | |
| ui::PixelFormat* format) { | |
| if (!resolution.isValid()) { | |
| ALOGE("%s: Invalid resolution %dx%d", __func__, resolution.width, resolution.height); | |
| return false; | |
| } | |
| const uint32_t width = static_cast<uint32_t>(resolution.width); | |
| const uint32_t height = static_cast<uint32_t>(resolution.height); | |
| if (mMaxVirtualDisplayDimension > 0 && | |
| (width > mMaxVirtualDisplayDimension || height > mMaxVirtualDisplayDimension)) { | |
| ALOGE("%s: Resolution %ux%u exceeds maximum dimension %zu", __func__, width, height, | |
| mMaxVirtualDisplayDimension); | |
| return false; | |
| } | |
| hal::HWDisplayId hwcDisplayId; | |
| const auto error = static_cast<hal::Error>( | |
| mComposer->createVirtualDisplay(width, height, format, &hwcDisplayId)); | |
| RETURN_IF_HWC_ERROR_FOR("createVirtualDisplay", error, displayId, false); | |
| auto display = std::make_unique<HWC2::impl::Display>(*mComposer.get(), mCapabilities, | |
| hwcDisplayId, hal::DisplayType::VIRTUAL); | |
| display->setConnected(true); | |
| auto& displayData = mDisplayData[displayId]; | |
| displayData.hwcDisplay = std::move(display); | |
| return true; | |
| } | |
| void HWComposer::allocatePhysicalDisplay(hal::HWDisplayId hwcDisplayId, PhysicalDisplayId displayId, | |
| uint8_t port, std::optional<ui::Size> physicalSize) { | |
| LOG_ALWAYS_FATAL_IF(!mActivePorts.try_emplace(port).second, | |
| "Cannot attach display %" PRIu64 " to an already active port %" PRIu8 ".", | |
| hwcDisplayId, port); | |
| mPhysicalDisplayIdMap[hwcDisplayId] = displayId; | |
| if (!mPrimaryHwcDisplayId) { | |
| mPrimaryHwcDisplayId = hwcDisplayId; | |
| } | |
| auto& displayData = mDisplayData[displayId]; | |
| auto newDisplay = | |
| std::make_unique<HWC2::impl::Display>(*mComposer.get(), mCapabilities, hwcDisplayId, | |
| hal::DisplayType::PHYSICAL); | |
| newDisplay->setConnected(true); | |
| newDisplay->setPhysicalSizeInMm(physicalSize); | |
| displayData.hwcDisplay = std::move(newDisplay); | |
| displayData.port = port; | |
| } | |
| int32_t HWComposer::getAttribute(hal::HWDisplayId hwcDisplayId, hal::HWConfigId configId, | |
| hal::Attribute attribute) const { | |
| int32_t value = 0; | |
| auto error = static_cast<hal::Error>( | |
| mComposer->getDisplayAttribute(hwcDisplayId, configId, attribute, &value)); | |
| RETURN_IF_HWC_ERROR_FOR("getDisplayAttribute", error, *toPhysicalDisplayId(hwcDisplayId), -1); | |
| return value; | |
| } | |
| std::shared_ptr<HWC2::Layer> HWComposer::createLayer(HalDisplayId displayId) { | |
| RETURN_IF_INVALID_DISPLAY(displayId, nullptr); | |
| auto expected = mDisplayData[displayId].hwcDisplay->createLayer(); | |
| if (!expected.has_value()) { | |
| auto error = std::move(expected).error(); | |
| RETURN_IF_HWC_ERROR(error, displayId, nullptr); | |
| } | |
| return std::move(expected).value(); | |
| } | |
| bool HWComposer::isConnected(PhysicalDisplayId displayId) const { | |
| return mDisplayData.count(displayId) && mDisplayData.at(displayId).hwcDisplay->isConnected(); | |
| } | |
| std::vector<HWComposer::HWCDisplayMode> HWComposer::getModes(PhysicalDisplayId displayId, | |
| int32_t maxFrameIntervalNs) const { | |
| RETURN_IF_INVALID_DISPLAY(displayId, {}); | |
| const auto hwcDisplayId = mDisplayData.at(displayId).hwcDisplay->getId(); | |
| if (mComposer->isVrrSupported()) { | |
| return getModesFromDisplayConfigurations(hwcDisplayId, maxFrameIntervalNs); | |
| } | |
| return getModesFromLegacyDisplayConfigs(hwcDisplayId); | |
| } | |
| DisplayConfiguration::Dpi HWComposer::getEstimatedDotsPerInchFromSize( | |
| uint64_t hwcDisplayId, const HWCDisplayMode& hwcMode) const { | |
| if (!FlagManager::getInstance().correct_dpi_with_display_size()) { | |
| return {-1, -1}; | |
| } | |
| if (const auto displayId = toPhysicalDisplayId(hwcDisplayId)) { | |
| if (const auto it = mDisplayData.find(displayId.value()); | |
| it != mDisplayData.end() && it->second.hwcDisplay->getPhysicalSizeInMm()) { | |
| ui::Size size = it->second.hwcDisplay->getPhysicalSizeInMm().value(); | |
| if (hwcMode.width > 0 && hwcMode.height > 0 && size.width > 0 && size.height > 0) { | |
| static constexpr float kMmPerInch = 25.4f; | |
| return {hwcMode.width * kMmPerInch / size.width, | |
| hwcMode.height * kMmPerInch / size.height}; | |
| } | |
| } | |
| } | |
| return {-1, -1}; | |
| } | |
| DisplayConfiguration::Dpi HWComposer::correctedDpiIfneeded( | |
| DisplayConfiguration::Dpi dpi, DisplayConfiguration::Dpi estimatedDpi) const { | |
| // hwc can be unreliable when it comes to dpi. A rough estimated dpi may yield better | |
| // results. For instance, libdrm and bad edid may result in a dpi of {350, 290} for a | |
| // 16:9 3840x2160 display, which would match a 4:3 aspect ratio. | |
| // The logic here checks if hwc was able to provide some dpi, and if so if the dpi | |
| // disparity between the axes is more reasonable than a rough estimate, otherwise use | |
| // the estimated dpi as a corrected value. | |
| if (estimatedDpi.x == -1 || estimatedDpi.y == -1) { | |
| return dpi; | |
| } | |
| if (dpi.x == -1 || dpi.y == -1) { | |
| return estimatedDpi; | |
| } | |
| if (std::min(dpi.x, dpi.y) != 0 && std::min(estimatedDpi.x, estimatedDpi.y) != 0 && | |
| abs(dpi.x - dpi.y) / std::min(dpi.x, dpi.y) > | |
| abs(estimatedDpi.x - estimatedDpi.y) / std::min(estimatedDpi.x, estimatedDpi.y)) { | |
| return estimatedDpi; | |
| } | |
| return dpi; | |
| } | |
| std::vector<HWComposer::HWCDisplayMode> HWComposer::getModesFromDisplayConfigurations( | |
| uint64_t hwcDisplayId, int32_t maxFrameIntervalNs) const { | |
| std::vector<hal::DisplayConfiguration> configs; | |
| auto error = static_cast<hal::Error>( | |
| mComposer->getDisplayConfigurations(hwcDisplayId, maxFrameIntervalNs, &configs)); | |
| RETURN_IF_HWC_ERROR_FOR("getDisplayConfigurations", error, *toPhysicalDisplayId(hwcDisplayId), | |
| {}); | |
| std::vector<HWCDisplayMode> modes; | |
| modes.reserve(configs.size()); | |
| for (auto config : configs) { | |
| auto hwcMode = HWCDisplayMode{.hwcId = static_cast<hal::HWConfigId>(config.configId), | |
| .width = config.width, | |
| .height = config.height, | |
| .vsyncPeriod = config.vsyncPeriod, | |
| .configGroup = config.configGroup, | |
| .vrrConfig = config.vrrConfig, | |
| .hdrOutputType = config.hdrOutputType}; | |
| const DisplayConfiguration::Dpi estimatedDPI = | |
| getEstimatedDotsPerInchFromSize(hwcDisplayId, hwcMode); | |
| if (config.dpi) { | |
| const DisplayConfiguration::Dpi dpi = | |
| correctedDpiIfneeded(config.dpi.value(), estimatedDPI); | |
| hwcMode.dpiX = dpi.x; | |
| hwcMode.dpiY = dpi.y; | |
| } else if (estimatedDPI.x != -1 && estimatedDPI.y != -1) { | |
| hwcMode.dpiX = estimatedDPI.x; | |
| hwcMode.dpiY = estimatedDPI.y; | |
| } | |
| if (!mEnableVrrTimeout) { | |
| hwcMode.vrrConfig->notifyExpectedPresentConfig = {}; | |
| } | |
| modes.push_back(hwcMode); | |
| } | |
| return modes; | |
| } | |
| std::vector<HWComposer::HWCDisplayMode> HWComposer::getModesFromLegacyDisplayConfigs( | |
| uint64_t hwcDisplayId) const { | |
| std::vector<hal::HWConfigId> configIds; | |
| auto error = static_cast<hal::Error>(mComposer->getDisplayConfigs(hwcDisplayId, &configIds)); | |
| RETURN_IF_HWC_ERROR_FOR("getDisplayConfigs", error, *toPhysicalDisplayId(hwcDisplayId), {}); | |
| std::vector<HWCDisplayMode> modes; | |
| modes.reserve(configIds.size()); | |
| for (auto configId : configIds) { | |
| auto hwcMode = HWCDisplayMode{ | |
| .hwcId = configId, | |
| .width = getAttribute(hwcDisplayId, configId, hal::Attribute::WIDTH), | |
| .height = getAttribute(hwcDisplayId, configId, hal::Attribute::HEIGHT), | |
| .vsyncPeriod = getAttribute(hwcDisplayId, configId, hal::Attribute::VSYNC_PERIOD), | |
| .configGroup = getAttribute(hwcDisplayId, configId, hal::Attribute::CONFIG_GROUP), | |
| }; | |
| const int32_t dpiX = getAttribute(hwcDisplayId, configId, hal::Attribute::DPI_X); | |
| const int32_t dpiY = getAttribute(hwcDisplayId, configId, hal::Attribute::DPI_Y); | |
| const DisplayConfiguration::Dpi hwcDpi = | |
| DisplayConfiguration::Dpi{dpiX == -1 ? dpiX : dpiX / 1000.f, | |
| dpiY == -1 ? dpiY : dpiY / 1000.f}; | |
| const DisplayConfiguration::Dpi estimatedDPI = | |
| getEstimatedDotsPerInchFromSize(hwcDisplayId, hwcMode); | |
| const DisplayConfiguration::Dpi dpi = correctedDpiIfneeded(hwcDpi, estimatedDPI); | |
| hwcMode.dpiX = dpi.x; | |
| hwcMode.dpiY = dpi.y; | |
| modes.push_back(hwcMode); | |
| } | |
| return modes; | |
| } | |
| ftl::Expected<hal::HWConfigId, status_t> HWComposer::getActiveMode( | |
| PhysicalDisplayId displayId) const { | |
| RETURN_IF_INVALID_DISPLAY(displayId, ftl::Unexpected(BAD_INDEX)); | |
| const auto hwcId = *fromPhysicalDisplayId(displayId); | |
| hal::HWConfigId configId; | |
| const auto error = static_cast<hal::Error>(mComposer->getActiveConfig(hwcId, &configId)); | |
| if (error == hal::Error::BAD_CONFIG) { | |
| return ftl::Unexpected(NO_INIT); | |
| } | |
| RETURN_IF_HWC_ERROR_FOR("getActiveConfig", error, displayId, ftl::Unexpected(UNKNOWN_ERROR)); | |
| return configId; | |
| } | |
| // Composer 2.4 | |
| ui::DisplayConnectionType HWComposer::getDisplayConnectionType(PhysicalDisplayId displayId) const { | |
| RETURN_IF_INVALID_DISPLAY(displayId, ui::DisplayConnectionType::Internal); | |
| const auto& hwcDisplay = mDisplayData.at(displayId).hwcDisplay; | |
| if (const auto connectionType = hwcDisplay->getConnectionType()) { | |
| return connectionType.value(); | |
| } else { | |
| LOG_HWC_ERROR(__func__, connectionType.error(), displayId); | |
| return hwcDisplay->getId() == mPrimaryHwcDisplayId ? ui::DisplayConnectionType::Internal | |
| : ui::DisplayConnectionType::External; | |
| } | |
| } | |
| bool HWComposer::isVsyncPeriodSwitchSupported(PhysicalDisplayId displayId) const { | |
| RETURN_IF_INVALID_DISPLAY(displayId, false); | |
| return mDisplayData.at(displayId).hwcDisplay->isVsyncPeriodSwitchSupported(); | |
| } | |
| ftl::Expected<nsecs_t, status_t> HWComposer::getDisplayVsyncPeriod( | |
| PhysicalDisplayId displayId) const { | |
| RETURN_IF_INVALID_DISPLAY(displayId, ftl::Unexpected(BAD_INDEX)); | |
| if (!isVsyncPeriodSwitchSupported(displayId)) { | |
| return ftl::Unexpected(INVALID_OPERATION); | |
| } | |
| const auto hwcId = *fromPhysicalDisplayId(displayId); | |
| Hwc2::VsyncPeriodNanos vsyncPeriodNanos = 0; | |
| const auto error = | |
| static_cast<hal::Error>(mComposer->getDisplayVsyncPeriod(hwcId, &vsyncPeriodNanos)); | |
| RETURN_IF_HWC_ERROR(error, displayId, ftl::Unexpected(UNKNOWN_ERROR)); | |
| return static_cast<nsecs_t>(vsyncPeriodNanos); | |
| } | |
| std::vector<ui::ColorMode> HWComposer::getColorModes(PhysicalDisplayId displayId) const { | |
| RETURN_IF_INVALID_DISPLAY(displayId, {}); | |
| std::vector<ui::ColorMode> modes; | |
| auto error = mDisplayData.at(displayId).hwcDisplay->getColorModes(&modes); | |
| RETURN_IF_HWC_ERROR(error, displayId, {}); | |
| return modes; | |
| } | |
| status_t HWComposer::setActiveColorMode(PhysicalDisplayId displayId, ui::ColorMode mode, | |
| ui::RenderIntent renderIntent) { | |
| RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); | |
| auto& displayData = mDisplayData[displayId]; | |
| auto error = displayData.hwcDisplay->setColorMode(mode, renderIntent); | |
| RETURN_IF_HWC_ERROR_FOR(("setColorMode(" + decodeColorMode(mode) + ", " + | |
| decodeRenderIntent(renderIntent) + ")") | |
| .c_str(), | |
| error, displayId, UNKNOWN_ERROR); | |
| return NO_ERROR; | |
| } | |
| void HWComposer::setVsyncEnabled(PhysicalDisplayId displayId, hal::Vsync enabled) { | |
| RETURN_IF_INVALID_DISPLAY(displayId); | |
| auto& displayData = mDisplayData[displayId]; | |
| // NOTE: we use our own internal lock here because we have to call | |
| // into the HWC with the lock held, and we want to make sure | |
| // that even if HWC blocks (which it shouldn't), it won't | |
| // affect other threads. | |
| std::lock_guard lock(displayData.vsyncEnabledLock); | |
| if (enabled == displayData.vsyncEnabled) { | |
| return; | |
| } | |
| SFTRACE_CALL(); | |
| auto error = displayData.hwcDisplay->setVsyncEnabled(enabled); | |
| RETURN_IF_HWC_ERROR(error, displayId); | |
| displayData.vsyncEnabled = enabled; | |
| SFTRACE_INT(ftl::Concat("HW_VSYNC_ON_", displayId.value).c_str(), | |
| enabled == hal::Vsync::ENABLE ? 1 : 0); | |
| } | |
| status_t HWComposer::setClientTarget(HalDisplayId displayId, uint32_t slot, | |
| const sp<Fence>& acquireFence, const sp<GraphicBuffer>& target, | |
| ui::Dataspace dataspace, float hdrSdrRatio) { | |
| RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); | |
| ALOGV("%s for display %s", __FUNCTION__, to_string(displayId).c_str()); | |
| auto& hwcDisplay = mDisplayData[displayId].hwcDisplay; | |
| auto error = hwcDisplay->setClientTarget(slot, target, acquireFence, dataspace, hdrSdrRatio); | |
| RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE); | |
| return NO_ERROR; | |
| } | |
| status_t HWComposer::getDeviceCompositionChanges( | |
| HalDisplayId displayId, bool frameUsesClientComposition, | |
| std::optional<std::chrono::steady_clock::time_point> earliestPresentTime, | |
| nsecs_t expectedPresentTime, Fps frameInterval, | |
| std::optional<android::HWComposer::DeviceRequestedChanges>* outChanges) { | |
| SFTRACE_CALL(); | |
| RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); | |
| auto& displayData = mDisplayData[displayId]; | |
| auto& hwcDisplay = displayData.hwcDisplay; | |
| if (!hwcDisplay->isConnected()) { | |
| return NO_ERROR; | |
| } | |
| uint32_t numTypes = 0; | |
| uint32_t numRequests = 0; | |
| hal::Error error = hal::Error::NONE; | |
| // First try to skip validate altogether. We can do that when | |
| // 1. The previous frame has not been presented yet or already passed the | |
| // earliest time to present. Otherwise, we may present a frame too early. | |
| // 2. There is no client composition. Otherwise, we first need to render the | |
| // client target buffer. | |
| const bool canSkipValidate = [&] { | |
| // We must call validate if we have client composition | |
| if (frameUsesClientComposition) { | |
| return false; | |
| } | |
| // If composer supports getting the expected present time, we can skip | |
| // as composer will make sure to prevent early presentation | |
| if (!earliestPresentTime) { | |
| return true; | |
| } | |
| // composer doesn't support getting the expected present time. We can only | |
| // skip validate if we know that we are not going to present early. | |
| return std::chrono::steady_clock::now() >= *earliestPresentTime; | |
| }(); | |
| displayData.validateWasSkipped = false; | |
| SFTRACE_FORMAT("NextFrameInterval %d_Hz", frameInterval.getIntValue()); | |
| if (canSkipValidate) { | |
| sp<Fence> outPresentFence = Fence::NO_FENCE; | |
| uint32_t state = UINT32_MAX; | |
| error = hwcDisplay->presentOrValidate(expectedPresentTime, frameInterval.getPeriodNsecs(), | |
| &numTypes, &numRequests, &outPresentFence, &state); | |
| if (!hasChangesError(error)) { | |
| RETURN_IF_HWC_ERROR_FOR("presentOrValidate", error, displayId, UNKNOWN_ERROR); | |
| } | |
| if (state == 1) { // Present Succeeded. | |
| std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences; | |
| error = hwcDisplay->getReleaseFences(&releaseFences); | |
| displayData.releaseFences = std::move(releaseFences); | |
| displayData.lastPresentFence = outPresentFence; | |
| displayData.validateWasSkipped = true; | |
| displayData.presentError = error; | |
| return NO_ERROR; | |
| } | |
| // Present failed but Validate ran. | |
| } else { | |
| error = hwcDisplay->validate(expectedPresentTime, frameInterval.getPeriodNsecs(), &numTypes, | |
| &numRequests); | |
| } | |
| ALOGV("SkipValidate failed, Falling back to SLOW validate/present"); | |
| if (!hasChangesError(error)) { | |
| RETURN_IF_HWC_ERROR_FOR("validate", error, displayId, BAD_INDEX); | |
| } | |
| android::HWComposer::DeviceRequestedChanges::ChangedTypes changedTypes; | |
| changedTypes.reserve(numTypes); | |
| error = hwcDisplay->getChangedCompositionTypes(&changedTypes); | |
| RETURN_IF_HWC_ERROR_FOR("getChangedCompositionTypes", error, displayId, BAD_INDEX); | |
| auto displayRequests = static_cast<hal::DisplayRequest>(0); | |
| android::HWComposer::DeviceRequestedChanges::LayerRequests layerRequests; | |
| layerRequests.reserve(numRequests); | |
| error = hwcDisplay->getRequests(&displayRequests, &layerRequests); | |
| RETURN_IF_HWC_ERROR_FOR("getRequests", error, displayId, BAD_INDEX); | |
| DeviceRequestedChanges::ClientTargetProperty clientTargetProperty; | |
| error = hwcDisplay->getClientTargetProperty(&clientTargetProperty); | |
| RETURN_IF_HWC_ERROR_FOR("getClientTargetProperty", error, displayId, BAD_INDEX); | |
| DeviceRequestedChanges::LayerLuts layerLuts; | |
| error = hwcDisplay->getRequestedLuts(&layerLuts, mLutFileDescriptorMapper); | |
| RETURN_IF_HWC_ERROR_FOR("getRequestedLuts", error, displayId, BAD_INDEX); | |
| outChanges->emplace(DeviceRequestedChanges{std::move(changedTypes), std::move(displayRequests), | |
| std::move(layerRequests), | |
| std::move(clientTargetProperty), | |
| std::move(layerLuts)}); | |
| error = hwcDisplay->acceptChanges(); | |
| RETURN_IF_HWC_ERROR_FOR("acceptChanges", error, displayId, BAD_INDEX); | |
| return NO_ERROR; | |
| } | |
| sp<Fence> HWComposer::getPresentFence(HalDisplayId displayId) const { | |
| RETURN_IF_INVALID_DISPLAY(displayId, Fence::NO_FENCE); | |
| return mDisplayData.at(displayId).lastPresentFence; | |
| } | |
| nsecs_t HWComposer::getPresentTimestamp(PhysicalDisplayId displayId) const { | |
| RETURN_IF_INVALID_DISPLAY(displayId, 0); | |
| return mDisplayData.at(displayId).lastPresentTimestamp; | |
| } | |
| sp<Fence> HWComposer::getLayerReleaseFence(HalDisplayId displayId, HWC2::Layer* layer) const { | |
| RETURN_IF_INVALID_DISPLAY(displayId, Fence::NO_FENCE); | |
| const auto& displayFences = mDisplayData.at(displayId).releaseFences; | |
| auto fence = displayFences.find(layer); | |
| if (fence == displayFences.end()) { | |
| ALOGV("getLayerReleaseFence: Release fence not found"); | |
| return Fence::NO_FENCE; | |
| } | |
| return fence->second; | |
| } | |
| status_t HWComposer::presentAndGetReleaseFences( | |
| HalDisplayId displayId, | |
| std::optional<std::chrono::steady_clock::time_point> earliestPresentTime) { | |
| SFTRACE_CALL(); | |
| RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); | |
| auto& displayData = mDisplayData[displayId]; | |
| auto& hwcDisplay = displayData.hwcDisplay; | |
| if (displayData.validateWasSkipped) { | |
| // explicitly flush all pending commands | |
| auto error = static_cast<hal::Error>(mComposer->executeCommands(hwcDisplay->getId())); | |
| RETURN_IF_HWC_ERROR_FOR("executeCommands", error, displayId, UNKNOWN_ERROR); | |
| RETURN_IF_HWC_ERROR_FOR("present", displayData.presentError, displayId, UNKNOWN_ERROR); | |
| return NO_ERROR; | |
| } | |
| if (earliestPresentTime) { | |
| SFTRACE_NAME("wait for earliest present time"); | |
| std::this_thread::sleep_until(*earliestPresentTime); | |
| } | |
| auto error = hwcDisplay->present(&displayData.lastPresentFence); | |
| RETURN_IF_HWC_ERROR_FOR("present", error, displayId, UNKNOWN_ERROR); | |
| std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences; | |
| error = hwcDisplay->getReleaseFences(&releaseFences); | |
| RETURN_IF_HWC_ERROR_FOR("getReleaseFences", error, displayId, UNKNOWN_ERROR); | |
| displayData.releaseFences = std::move(releaseFences); | |
| return NO_ERROR; | |
| } | |
| status_t HWComposer::executeCommands(HalDisplayId displayId) { | |
| auto& hwcDisplay = mDisplayData[displayId].hwcDisplay; | |
| auto error = static_cast<hal::Error>(mComposer->executeCommands(hwcDisplay->getId())); | |
| RETURN_IF_HWC_ERROR_FOR("executeCommands", error, displayId, UNKNOWN_ERROR); | |
| return NO_ERROR; | |
| } | |
| status_t HWComposer::setPowerMode(PhysicalDisplayId displayId, hal::PowerMode mode) { | |
| RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); | |
| if (mode == hal::PowerMode::OFF) { | |
| setVsyncEnabled(displayId, hal::Vsync::DISABLE); | |
| } | |
| const auto& displayData = mDisplayData[displayId]; | |
| auto& hwcDisplay = displayData.hwcDisplay; | |
| switch (mode) { | |
| case hal::PowerMode::OFF: | |
| case hal::PowerMode::ON: | |
| ALOGV("setPowerMode: Calling HWC %s", to_string(mode).c_str()); | |
| { | |
| auto error = hwcDisplay->setPowerMode(mode); | |
| if (error != hal::Error::NONE) { | |
| LOG_HWC_ERROR(("setPowerMode(" + to_string(mode) + ")").c_str(), error, | |
| displayId); | |
| } | |
| } | |
| break; | |
| case hal::PowerMode::DOZE: | |
| case hal::PowerMode::DOZE_SUSPEND: | |
| ALOGV("setPowerMode: Calling HWC %s", to_string(mode).c_str()); | |
| { | |
| bool supportsDoze = false; | |
| const auto queryDozeError = hwcDisplay->supportsDoze(&supportsDoze); | |
| // queryDozeError might be NO_RESOURCES, in the case of a display that has never | |
| // been turned on. In that case, attempt to set to DOZE anyway. | |
| if (!supportsDoze && queryDozeError == hal::Error::NONE) { | |
| mode = hal::PowerMode::ON; | |
| } | |
| auto error = hwcDisplay->setPowerMode(mode); | |
| if (error != hal::Error::NONE) { | |
| LOG_HWC_ERROR(("setPowerMode(" + to_string(mode) + ")").c_str(), error, | |
| displayId); | |
| // If the display had never been turned on, so its doze | |
| // support was unknown, it may truly not support doze. Try | |
| // switching it to ON instead. | |
| if (queryDozeError == hal::Error::NO_RESOURCES) { | |
| ALOGD("%s: failed to set %s to %s. Trying again with ON", __func__, | |
| to_string(displayId).c_str(), to_string(mode).c_str()); | |
| error = hwcDisplay->setPowerMode(hal::PowerMode::ON); | |
| if (error != hal::Error::NONE) { | |
| LOG_HWC_ERROR("setPowerMode(ON)", error, displayId); | |
| } | |
| } | |
| } | |
| } | |
| break; | |
| default: | |
| ALOGV("setPowerMode: Not calling HWC"); | |
| break; | |
| } | |
| return NO_ERROR; | |
| } | |
| status_t HWComposer::setActiveModeWithConstraints( | |
| PhysicalDisplayId displayId, hal::HWConfigId hwcModeId, | |
| const hal::VsyncPeriodChangeConstraints& constraints, | |
| hal::VsyncPeriodChangeTimeline* outTimeline) { | |
| RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); | |
| auto error = mDisplayData[displayId].hwcDisplay->setActiveConfigWithConstraints(hwcModeId, | |
| constraints, | |
| outTimeline); | |
| if (error == hal::Error::CONFIG_FAILED) { | |
| RETURN_IF_HWC_ERROR_FOR("setActiveConfigWithConstraints", error, displayId, | |
| FAILED_TRANSACTION); | |
| } | |
| RETURN_IF_HWC_ERROR_FOR("setActiveConfigWithConstraints", error, displayId, UNKNOWN_ERROR); | |
| return NO_ERROR; | |
| } | |
| status_t HWComposer::setColorTransform(HalDisplayId displayId, const mat4& transform) { | |
| RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); | |
| auto& displayData = mDisplayData[displayId]; | |
| auto error = displayData.hwcDisplay->setColorTransform(transform); | |
| RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR); | |
| return NO_ERROR; | |
| } | |
| void HWComposer::disconnectDisplay(HalDisplayId displayId) { | |
| RETURN_IF_INVALID_DISPLAY(displayId); | |
| auto& displayData = mDisplayData[displayId]; | |
| const auto hwcDisplayId = displayData.hwcDisplay->getId(); | |
| mPhysicalDisplayIdMap.erase(hwcDisplayId); | |
| if (const auto port = displayData.port) { | |
| mActivePorts.erase(port.value()); | |
| } | |
| mDisplayData.erase(displayId); | |
| // Reset the primary display ID if we're disconnecting it. | |
| // This way isHeadless() will return false, which is necessary | |
| // because getPrimaryDisplayId() will crash. | |
| if (mPrimaryHwcDisplayId == hwcDisplayId) { | |
| mPrimaryHwcDisplayId.reset(); | |
| } | |
| } | |
| status_t HWComposer::setOutputBuffer(HalVirtualDisplayId displayId, const sp<Fence>& acquireFence, | |
| const sp<GraphicBuffer>& buffer) { | |
| RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); | |
| const auto& displayData = mDisplayData[displayId]; | |
| auto error = displayData.hwcDisplay->setOutputBuffer(buffer, acquireFence); | |
| RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR); | |
| return NO_ERROR; | |
| } | |
| void HWComposer::clearReleaseFences(HalDisplayId displayId) { | |
| RETURN_IF_INVALID_DISPLAY(displayId); | |
| mDisplayData[displayId].releaseFences.clear(); | |
| } | |
| status_t HWComposer::getHdrCapabilities(HalDisplayId displayId, HdrCapabilities* outCapabilities) { | |
| RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); | |
| auto& hwcDisplay = mDisplayData[displayId].hwcDisplay; | |
| auto error = hwcDisplay->getHdrCapabilities(outCapabilities); | |
| RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR); | |
| return NO_ERROR; | |
| } | |
| const aidl::android::hardware::graphics::composer3::OverlayProperties& | |
| HWComposer::getOverlaySupport() const { | |
| return mOverlayProperties; | |
| } | |
| int32_t HWComposer::getSupportedPerFrameMetadata(HalDisplayId displayId) const { | |
| RETURN_IF_INVALID_DISPLAY(displayId, 0); | |
| return mDisplayData.at(displayId).hwcDisplay->getSupportedPerFrameMetadata(); | |
| } | |
| std::vector<ui::RenderIntent> HWComposer::getRenderIntents(HalDisplayId displayId, | |
| ui::ColorMode colorMode) const { | |
| RETURN_IF_INVALID_DISPLAY(displayId, {}); | |
| std::vector<ui::RenderIntent> renderIntents; | |
| auto error = mDisplayData.at(displayId).hwcDisplay->getRenderIntents(colorMode, &renderIntents); | |
| RETURN_IF_HWC_ERROR(error, displayId, {}); | |
| return renderIntents; | |
| } | |
| mat4 HWComposer::getDataspaceSaturationMatrix(HalDisplayId displayId, ui::Dataspace dataspace) { | |
| RETURN_IF_INVALID_DISPLAY(displayId, {}); | |
| mat4 matrix; | |
| auto error = | |
| mDisplayData[displayId].hwcDisplay->getDataspaceSaturationMatrix(dataspace, &matrix); | |
| RETURN_IF_HWC_ERROR(error, displayId, {}); | |
| return matrix; | |
| } | |
| status_t HWComposer::getDisplayedContentSamplingAttributes(HalDisplayId displayId, | |
| ui::PixelFormat* outFormat, | |
| ui::Dataspace* outDataspace, | |
| uint8_t* outComponentMask) { | |
| RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); | |
| const auto error = | |
| mDisplayData[displayId] | |
| .hwcDisplay->getDisplayedContentSamplingAttributes(outFormat, outDataspace, | |
| outComponentMask); | |
| if (error == hal::Error::UNSUPPORTED) RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION); | |
| RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR); | |
| return NO_ERROR; | |
| } | |
| status_t HWComposer::setDisplayContentSamplingEnabled(HalDisplayId displayId, bool enabled, | |
| uint8_t componentMask, uint64_t maxFrames) { | |
| RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); | |
| const auto error = | |
| mDisplayData[displayId].hwcDisplay->setDisplayContentSamplingEnabled(enabled, | |
| componentMask, | |
| maxFrames); | |
| if (error == hal::Error::UNSUPPORTED) RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION); | |
| if (error == hal::Error::BAD_PARAMETER) RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE); | |
| RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR); | |
| return NO_ERROR; | |
| } | |
| status_t HWComposer::getDisplayedContentSample(HalDisplayId displayId, uint64_t maxFrames, | |
| uint64_t timestamp, DisplayedFrameStats* outStats) { | |
| RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); | |
| const auto error = | |
| mDisplayData[displayId].hwcDisplay->getDisplayedContentSample(maxFrames, timestamp, | |
| outStats); | |
| RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR); | |
| return NO_ERROR; | |
| } | |
| ftl::Future<status_t> HWComposer::setDisplayBrightness( | |
| PhysicalDisplayId displayId, float brightness, float brightnessNits, | |
| const Hwc2::Composer::DisplayBrightnessOptions& options) { | |
| RETURN_IF_INVALID_DISPLAY(displayId, ftl::yield<status_t>(BAD_INDEX)); | |
| auto& display = mDisplayData[displayId].hwcDisplay; | |
| return display->setDisplayBrightness(brightness, brightnessNits, options) | |
| .then([displayId](hal::Error error) -> status_t { | |
| if (error == hal::Error::UNSUPPORTED) { | |
| RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION); | |
| } | |
| if (error == hal::Error::BAD_PARAMETER) { | |
| RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE); | |
| } | |
| RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR); | |
| return NO_ERROR; | |
| }); | |
| } | |
| bool HWComposer::getValidateSkipped(HalDisplayId displayId) const { | |
| if (mDisplayData.count(displayId) == 0) { | |
| return false; | |
| } | |
| return mDisplayData.at(displayId).validateWasSkipped; | |
| } | |
| status_t HWComposer::setBootDisplayMode(PhysicalDisplayId displayId, | |
| hal::HWConfigId displayModeId) { | |
| RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); | |
| const auto error = mDisplayData[displayId].hwcDisplay->setBootDisplayConfig(displayModeId); | |
| if (error == hal::Error::UNSUPPORTED) { | |
| RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION); | |
| } | |
| if (error == hal::Error::BAD_PARAMETER) { | |
| RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE); | |
| } | |
| RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR); | |
| return NO_ERROR; | |
| } | |
| status_t HWComposer::clearBootDisplayMode(PhysicalDisplayId displayId) { | |
| RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); | |
| const auto error = mDisplayData[displayId].hwcDisplay->clearBootDisplayConfig(); | |
| if (error == hal::Error::UNSUPPORTED) { | |
| RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION); | |
| } | |
| if (error == hal::Error::BAD_PARAMETER) { | |
| RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE); | |
| } | |
| RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR); | |
| return NO_ERROR; | |
| } | |
| std::optional<hal::HWConfigId> HWComposer::getPreferredBootDisplayMode( | |
| PhysicalDisplayId displayId) { | |
| RETURN_IF_INVALID_DISPLAY(displayId, std::nullopt); | |
| hal::HWConfigId displayModeId; | |
| const auto error = | |
| mDisplayData[displayId].hwcDisplay->getPreferredBootDisplayConfig(&displayModeId); | |
| if (error != hal::Error::NONE) { | |
| LOG_DISPLAY_ERROR(displayId, to_string(error).c_str()); | |
| return std::nullopt; | |
| } | |
| return displayModeId; | |
| } | |
| std::vector<HdrConversionCapability> HWComposer::getHdrConversionCapabilities() const { | |
| return mHdrConversionCapabilities; | |
| } | |
| status_t HWComposer::setHdrConversionStrategy( | |
| HdrConversionStrategy hdrConversionStrategy, | |
| aidl::android::hardware::graphics::common::Hdr* outPreferredHdrOutputType) { | |
| const auto error = | |
| mComposer->setHdrConversionStrategy(hdrConversionStrategy, outPreferredHdrOutputType); | |
| if (error != hal::Error::NONE) { | |
| ALOGE("Error in setting HDR conversion strategy %s", to_string(error).c_str()); | |
| return INVALID_OPERATION; | |
| } | |
| return NO_ERROR; | |
| } | |
| status_t HWComposer::setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId displayId, | |
| bool enabled) { | |
| RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); | |
| const auto error = | |
| mComposer->setRefreshRateChangedCallbackDebugEnabled(mDisplayData[displayId] | |
| .hwcDisplay->getId(), | |
| enabled); | |
| if (error != hal::Error::NONE) { | |
| ALOGE("Error in setting refresh refresh rate change callback debug enabled %s", | |
| to_string(error).c_str()); | |
| return INVALID_OPERATION; | |
| } | |
| return NO_ERROR; | |
| } | |
| status_t HWComposer::notifyExpectedPresent(PhysicalDisplayId displayId, | |
| TimePoint expectedPresentTime, Fps frameInterval) { | |
| RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); | |
| SFTRACE_FORMAT("%s ExpectedPresentTime in %.2fms frameInterval %.2fms", __func__, | |
| ticks<std::milli, float>(expectedPresentTime - TimePoint::now()), | |
| ticks<std::milli, float>(Duration::fromNs(frameInterval.getPeriodNsecs()))); | |
| const auto error = mComposer->notifyExpectedPresent(mDisplayData[displayId].hwcDisplay->getId(), | |
| expectedPresentTime.ns(), | |
| frameInterval.getPeriodNsecs()); | |
| if (error != hal::Error::NONE) { | |
| ALOGE("Error in notifyExpectedPresent call %s", to_string(error).c_str()); | |
| return INVALID_OPERATION; | |
| } | |
| return NO_ERROR; | |
| } | |
| status_t HWComposer::getDisplayDecorationSupport( | |
| PhysicalDisplayId displayId, | |
| std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>* | |
| support) { | |
| RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); | |
| const auto error = mDisplayData[displayId].hwcDisplay->getDisplayDecorationSupport(support); | |
| if (error == hal::Error::UNSUPPORTED) { | |
| RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION); | |
| } | |
| if (error == hal::Error::BAD_PARAMETER) { | |
| RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE); | |
| } | |
| RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR); | |
| return NO_ERROR; | |
| } | |
| status_t HWComposer::setAutoLowLatencyMode(PhysicalDisplayId displayId, bool on) { | |
| RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); | |
| const auto error = mDisplayData[displayId].hwcDisplay->setAutoLowLatencyMode(on); | |
| if (error == hal::Error::UNSUPPORTED) { | |
| RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION); | |
| } | |
| if (error == hal::Error::BAD_PARAMETER) { | |
| RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE); | |
| } | |
| RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR); | |
| return NO_ERROR; | |
| } | |
| status_t HWComposer::getSupportedContentTypes( | |
| PhysicalDisplayId displayId, | |
| std::vector<hal::ContentType>* outSupportedContentTypes) const { | |
| RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); | |
| const auto error = mDisplayData.at(displayId).hwcDisplay->getSupportedContentTypes( | |
| outSupportedContentTypes); | |
| RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR); | |
| return NO_ERROR; | |
| } | |
| status_t HWComposer::setContentType(PhysicalDisplayId displayId, hal::ContentType contentType) { | |
| RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); | |
| const auto error = mDisplayData[displayId].hwcDisplay->setContentType(contentType); | |
| if (error == hal::Error::UNSUPPORTED) { | |
| RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION); | |
| } | |
| if (error == hal::Error::BAD_PARAMETER) { | |
| RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE); | |
| } | |
| RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR); | |
| return NO_ERROR; | |
| } | |
| int32_t HWComposer::getMaxLayerPictureProfiles(PhysicalDisplayId displayId) { | |
| int32_t maxProfiles = 0; | |
| RETURN_IF_INVALID_DISPLAY(displayId, 0); | |
| const auto error = mDisplayData[displayId].hwcDisplay->getMaxLayerPictureProfiles(&maxProfiles); | |
| RETURN_IF_HWC_ERROR(error, displayId, 0); | |
| return maxProfiles; | |
| } | |
| status_t HWComposer::setDisplayPictureProfileHandle(PhysicalDisplayId displayId, | |
| const PictureProfileHandle& handle) { | |
| RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); | |
| const auto error = mDisplayData[displayId].hwcDisplay->setPictureProfileHandle(handle); | |
| if (error != hal::Error::UNSUPPORTED) { | |
| RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION); | |
| } | |
| return NO_ERROR; | |
| } | |
| status_t HWComposer::startHdcpNegotiation(PhysicalDisplayId displayId, | |
| const aidl::android::hardware::drm::HdcpLevels& levels) { | |
| RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); | |
| auto& hwcDisplay = mDisplayData[displayId].hwcDisplay; | |
| auto error = hwcDisplay->startHdcpNegotiation(levels); | |
| RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR); | |
| return NO_ERROR; | |
| } | |
| status_t HWComposer::getLuts( | |
| PhysicalDisplayId displayId, const std::vector<sp<GraphicBuffer>>& buffers, | |
| std::vector<aidl::android::hardware::graphics::composer3::Luts>* luts) { | |
| RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); | |
| auto& hwcDisplay = mDisplayData[displayId].hwcDisplay; | |
| auto error = hwcDisplay->getLuts(buffers, luts); | |
| RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR); | |
| return NO_ERROR; | |
| } | |
| const std::unordered_map<std::string, bool>& HWComposer::getSupportedLayerGenericMetadata() const { | |
| return mSupportedLayerGenericMetadata; | |
| } | |
| ftl::SmallMap<HWC2::Layer*, ::android::base::unique_fd, 20>& | |
| HWComposer::getLutFileDescriptorMapper() { | |
| return mLutFileDescriptorMapper; | |
| } | |
| void HWComposer::dumpOverlayProperties(std::string& result) const { | |
| // dump overlay properties | |
| result.append("OverlayProperties:\n"); | |
| base::StringAppendF(&result, "supportMixedColorSpaces: %d\n", | |
| mOverlayProperties.supportMixedColorSpaces); | |
| base::StringAppendF(&result, "SupportedBufferCombinations(%zu entries)\n", | |
| mOverlayProperties.combinations.size()); | |
| for (const auto& combination : mOverlayProperties.combinations) { | |
| result.append(" pixelFormats=\n"); | |
| for (const auto& pixelFormat : combination.pixelFormats) { | |
| base::StringAppendF(&result, " %s (%d)\n", | |
| decodePixelFormat(static_cast<PixelFormat>(pixelFormat)).c_str(), | |
| static_cast<uint32_t>(pixelFormat)); | |
| } | |
| result.append(" standards=\n"); | |
| for (const auto& standard : combination.standards) { | |
| base::StringAppendF(&result, " %s (%d)\n", | |
| decodeStandardOnly(static_cast<uint32_t>(standard)).c_str(), | |
| static_cast<uint32_t>(standard)); | |
| } | |
| result.append(" transfers=\n"); | |
| for (const auto& transfer : combination.transfers) { | |
| base::StringAppendF(&result, " %s (%d)\n", | |
| decodeTransferOnly(static_cast<uint32_t>(transfer)).c_str(), | |
| static_cast<uint32_t>(transfer)); | |
| } | |
| result.append(" ranges=\n"); | |
| for (const auto& range : combination.ranges) { | |
| base::StringAppendF(&result, " %s (%d)\n", | |
| decodeRangeOnly(static_cast<uint32_t>(range)).c_str(), | |
| static_cast<uint32_t>(range)); | |
| } | |
| result.append("\n"); | |
| } | |
| } | |
| void HWComposer::dump(std::string& result) const { | |
| result.append(mComposer->dumpDebugInfo()); | |
| dumpOverlayProperties(result); | |
| } | |
| std::optional<PhysicalDisplayId> HWComposer::toPhysicalDisplayId( | |
| hal::HWDisplayId hwcDisplayId) const { | |
| if (const auto it = mPhysicalDisplayIdMap.find(hwcDisplayId); | |
| it != mPhysicalDisplayIdMap.end()) { | |
| return it->second; | |
| } | |
| return {}; | |
| } | |
| std::optional<hal::HWDisplayId> HWComposer::fromPhysicalDisplayId( | |
| PhysicalDisplayId displayId) const { | |
| if (const auto it = mDisplayData.find(displayId); it != mDisplayData.end()) { | |
| return it->second.hwcDisplay->getId(); | |
| } | |
| return {}; | |
| } | |
| bool HWComposer::shouldIgnoreHotplugConnect(hal::HWDisplayId hwcDisplayId, uint8_t port, | |
| bool hasDisplayIdentificationData) const { | |
| if (mActivePorts.contains(port)) { | |
| ALOGE("Ignoring connection of display %" PRIu64 ". Port %" PRIu8 | |
| " is already in active use.", | |
| hwcDisplayId, port); | |
| return true; | |
| } | |
| if (mHasMultiDisplaySupport && !hasDisplayIdentificationData) { | |
| ALOGE("Ignoring connection of display %" PRIu64 " without identification data", | |
| hwcDisplayId); | |
| return true; | |
| } | |
| // Legacy mode only supports IDs LEGACY_DISPLAY_TYPE_PRIMARY and LEGACY_DISPLAY_TYPE_EXTERNAL. | |
| if (!mHasMultiDisplaySupport && mPhysicalDisplayIdMap.size() == 2) { | |
| ALOGE("Ignoring connection of tertiary display %" PRIu64, hwcDisplayId); | |
| return true; | |
| } | |
| return false; | |
| } | |
| std::optional<DisplayIdentificationInfo> HWComposer::onHotplugConnect( | |
| hal::HWDisplayId hwcDisplayId) { | |
| std::optional<DisplayIdentificationInfo> info; | |
| if (const auto displayId = toPhysicalDisplayId(hwcDisplayId)) { | |
| info = DisplayIdentificationInfo{.id = *displayId, | |
| .name = std::string(), | |
| .deviceProductInfo = std::nullopt}; | |
| if (mUpdateDeviceProductInfoOnHotplugReconnect) { | |
| uint8_t port; | |
| DisplayIdentificationData data; | |
| getDisplayIdentificationData(hwcDisplayId, &port, &data); | |
| if (auto newInfo = parseDisplayIdentificationData(port, data)) { | |
| info->deviceProductInfo = std::move(newInfo->deviceProductInfo); | |
| info->preferredDetailedTimingDescriptor = | |
| std::move(newInfo->preferredDetailedTimingDescriptor); | |
| } else { | |
| ALOGE("Failed to parse identification data for display %" PRIu64, hwcDisplayId); | |
| } | |
| } | |
| } else { | |
| uint8_t port; | |
| DisplayIdentificationData data; | |
| const bool hasDisplayIdentificationData = | |
| getDisplayIdentificationData(hwcDisplayId, &port, &data); | |
| if (mPhysicalDisplayIdMap.empty()) { | |
| mHasMultiDisplaySupport = hasDisplayIdentificationData; | |
| ALOGI("Switching to %s multi-display mode", | |
| mHasMultiDisplaySupport ? "generalized" : "legacy"); | |
| } | |
| if (shouldIgnoreHotplugConnect(hwcDisplayId, port, hasDisplayIdentificationData)) { | |
| return {}; | |
| } | |
| info = [this, hwcDisplayId, &port, &data, hasDisplayIdentificationData] { | |
| const bool isPrimary = !mPrimaryHwcDisplayId; | |
| if (mHasMultiDisplaySupport) { | |
| if (const auto info = parseDisplayIdentificationData(port, data)) { | |
| return *info; | |
| } | |
| ALOGE("Failed to parse identification data for display %" PRIu64, hwcDisplayId); | |
| } else { | |
| ALOGW_IF(hasDisplayIdentificationData, | |
| "Ignoring identification data for display %" PRIu64, hwcDisplayId); | |
| port = isPrimary ? LEGACY_DISPLAY_TYPE_PRIMARY : LEGACY_DISPLAY_TYPE_EXTERNAL; | |
| } | |
| return DisplayIdentificationInfo{.id = PhysicalDisplayId::fromPort(port), | |
| .name = isPrimary ? "Primary display" | |
| : "Secondary display", | |
| .port = port, | |
| .deviceProductInfo = std::nullopt}; | |
| }(); | |
| mComposer->onHotplugConnect(hwcDisplayId); | |
| } | |
| if (!isConnected(info->id)) { | |
| std::optional<ui::Size> size = std::nullopt; | |
| if (info->preferredDetailedTimingDescriptor) { | |
| size = info->preferredDetailedTimingDescriptor->physicalSizeInMm; | |
| } | |
| allocatePhysicalDisplay(hwcDisplayId, info->id, info->port, size); | |
| } | |
| return info; | |
| } | |
| std::optional<DisplayIdentificationInfo> HWComposer::onHotplugDisconnect( | |
| hal::HWDisplayId hwcDisplayId) { | |
| LOG_ALWAYS_FATAL_IF(hwcDisplayId == mPrimaryHwcDisplayId, | |
| "Primary display cannot be disconnected."); | |
| const auto displayId = toPhysicalDisplayId(hwcDisplayId); | |
| if (!displayId) { | |
| LOG_HWC_DISPLAY_ERROR(hwcDisplayId, "Invalid HWC display"); | |
| return {}; | |
| } | |
| if (!isConnected(*displayId)) { | |
| LOG_HWC_DISPLAY_ERROR(hwcDisplayId, "Already disconnected"); | |
| return {}; | |
| } | |
| // The display will later be destroyed by a call to HWComposer::disconnectDisplay. For now, mark | |
| // it as disconnected. | |
| mDisplayData.at(*displayId).hwcDisplay->setConnected(false); | |
| mComposer->onHotplugDisconnect(hwcDisplayId); | |
| return DisplayIdentificationInfo{.id = *displayId}; | |
| } | |
| std::optional<DisplayIdentificationInfo> HWComposer::onHotplugLinkTrainingFailure( | |
| hal::HWDisplayId hwcDisplayId) { | |
| const auto displayId = toPhysicalDisplayId(hwcDisplayId); | |
| if (!displayId) { | |
| LOG_HWC_DISPLAY_ERROR(hwcDisplayId, "Invalid HWC display"); | |
| return {}; | |
| } | |
| return DisplayIdentificationInfo{.id = *displayId}; | |
| } | |
| void HWComposer::loadCapabilities() { | |
| static_assert(sizeof(hal::Capability) == sizeof(int32_t), "Capability size has changed"); | |
| auto capabilities = mComposer->getCapabilities(); | |
| for (auto capability : capabilities) { | |
| mCapabilities.emplace(capability); | |
| } | |
| } | |
| void HWComposer::loadOverlayProperties() { | |
| mComposer->getOverlaySupport(&mOverlayProperties); | |
| } | |
| void HWComposer::loadHdrConversionCapabilities() { | |
| const auto error = mComposer->getHdrConversionCapabilities(&mHdrConversionCapabilities); | |
| if (error != hal::Error::NONE) { | |
| ALOGE("Error in fetching HDR conversion capabilities %s", to_string(error).c_str()); | |
| mHdrConversionCapabilities = {}; | |
| } | |
| } | |
| status_t HWComposer::setIdleTimerEnabled(PhysicalDisplayId displayId, | |
| std::chrono::milliseconds timeout) { | |
| SFTRACE_CALL(); | |
| RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); | |
| const auto error = mDisplayData[displayId].hwcDisplay->setIdleTimerEnabled(timeout); | |
| if (error == hal::Error::UNSUPPORTED) { | |
| RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION); | |
| } | |
| if (error == hal::Error::BAD_PARAMETER) { | |
| RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE); | |
| } | |
| RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR); | |
| return NO_ERROR; | |
| } | |
| bool HWComposer::hasDisplayIdleTimerCapability(PhysicalDisplayId displayId) const { | |
| RETURN_IF_INVALID_DISPLAY(displayId, false); | |
| return mDisplayData.at(displayId).hwcDisplay->hasDisplayIdleTimerCapability(); | |
| } | |
| Hwc2::AidlTransform HWComposer::getPhysicalDisplayOrientation(PhysicalDisplayId displayId) const { | |
| SFTRACE_CALL(); | |
| RETURN_IF_INVALID_DISPLAY(displayId, Hwc2::AidlTransform::NONE); | |
| Hwc2::AidlTransform outTransform; | |
| const auto& hwcDisplay = mDisplayData.at(displayId).hwcDisplay; | |
| const auto error = hwcDisplay->getPhysicalDisplayOrientation(&outTransform); | |
| RETURN_IF_HWC_ERROR(error, displayId, Hwc2::AidlTransform::NONE); | |
| return outTransform; | |
| } | |
| void HWComposer::loadLayerMetadataSupport() { | |
| mSupportedLayerGenericMetadata.clear(); | |
| std::vector<Hwc2::IComposerClient::LayerGenericMetadataKey> supportedMetadataKeyInfo; | |
| const auto error = mComposer->getLayerGenericMetadataKeys(&supportedMetadataKeyInfo); | |
| if (error != hardware::graphics::composer::V2_4::Error::NONE) { | |
| if (error != hardware::graphics::composer::V2_4::Error::UNSUPPORTED) { | |
| ALOGE("%s: %s failed: %s (%d)", __FUNCTION__, "getLayerGenericMetadataKeys", | |
| toString(error).c_str(), static_cast<int32_t>(error)); | |
| } | |
| return; | |
| } | |
| for (const auto& [name, mandatory] : supportedMetadataKeyInfo) { | |
| mSupportedLayerGenericMetadata.emplace(name, mandatory); | |
| } | |
| } | |
| } // namespace impl | |
| } // namespace android | |
| // TODO(b/129481165): remove the #pragma below and fix conversion issues | |
| #pragma clang diagnostic pop // ignored "-Wconversion" |