| /* |
| * Copyright 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. |
| */ |
| |
| #include "Display.h" |
| |
| #include <android-base/parseint.h> |
| #include <android-base/unique_fd.h> |
| #include <pthread.h> |
| #include <sched.h> |
| #include <sync/sync.h> |
| #include <sys/types.h> |
| |
| #include <algorithm> |
| #include <atomic> |
| #include <numeric> |
| #include <sstream> |
| #include <thread> |
| |
| #include "Common.h" |
| #include "Device.h" |
| |
| namespace aidl::android::hardware::graphics::composer3::impl { |
| namespace { |
| |
| bool isValidColorMode(ColorMode mode) { |
| switch (mode) { |
| case ColorMode::NATIVE: |
| case ColorMode::STANDARD_BT601_625: |
| case ColorMode::STANDARD_BT601_625_UNADJUSTED: |
| case ColorMode::STANDARD_BT601_525: |
| case ColorMode::STANDARD_BT601_525_UNADJUSTED: |
| case ColorMode::STANDARD_BT709: |
| case ColorMode::DCI_P3: |
| case ColorMode::SRGB: |
| case ColorMode::ADOBE_RGB: |
| case ColorMode::DISPLAY_P3: |
| case ColorMode::BT2020: |
| case ColorMode::BT2100_PQ: |
| case ColorMode::BT2100_HLG: |
| case ColorMode::DISPLAY_BT2020: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| bool isValidRenderIntent(RenderIntent intent) { |
| switch (intent) { |
| case RenderIntent::COLORIMETRIC: |
| case RenderIntent::ENHANCE: |
| case RenderIntent::TONE_MAP_COLORIMETRIC: |
| case RenderIntent::TONE_MAP_ENHANCE: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| bool isValidPowerMode(PowerMode mode) { |
| switch (mode) { |
| case PowerMode::OFF: |
| case PowerMode::DOZE: |
| case PowerMode::DOZE_SUSPEND: |
| case PowerMode::ON: |
| case PowerMode::ON_SUSPEND: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| } // namespace |
| |
| Display::Display(FrameComposer* composer, int64_t id) |
| : mComposer(composer), mId(id), mVsyncThread(id) { |
| setLegacyEdid(); |
| } |
| |
| Display::~Display() {} |
| |
| HWC3::Error Display::init(const std::vector<DisplayConfig>& configs, |
| int32_t activeConfigId, |
| const std::optional<std::vector<uint8_t>>& edid) { |
| std::unique_lock<std::recursive_mutex> lock(mStateMutex); |
| |
| for (const DisplayConfig& config : configs) { |
| mConfigs.emplace(config.getId(), config); |
| } |
| |
| mActiveConfigId = activeConfigId; |
| |
| auto bootConfigIdOpt = getBootConfigId(); |
| if (bootConfigIdOpt) { |
| mActiveConfigId = *bootConfigIdOpt; |
| } |
| |
| if (edid.has_value()) { |
| mEdid = *edid; |
| } |
| |
| auto it = mConfigs.find(activeConfigId); |
| if (it == mConfigs.end()) { |
| ALOGE("%s: display:%" PRId64 "missing config:%" PRId32, __FUNCTION__, mId, |
| activeConfigId); |
| return HWC3::Error::NoResources; |
| } |
| |
| const auto& activeConfig = it->second; |
| const auto activeConfigString = activeConfig.toString(); |
| ALOGD("%s display:%" PRId64 " with config:%s", __FUNCTION__, mId, |
| activeConfigString.c_str()); |
| |
| mVsyncThread.start(activeConfig.getVsyncPeriod()); |
| |
| return HWC3::Error::None; |
| } |
| |
| HWC3::Error Display::updateParameters( |
| uint32_t width, uint32_t height, uint32_t dpiX, uint32_t dpiY, |
| uint32_t refreshRateHz, const std::optional<std::vector<uint8_t>>& edid) { |
| DEBUG_LOG("%s: updating display:%" PRId64 |
| " width:%d height:%d dpiX:%d dpiY:%d refreshRateHz:%d", |
| __FUNCTION__, mId, width, height, dpiX, dpiY, refreshRateHz); |
| |
| std::unique_lock<std::recursive_mutex> lock(mStateMutex); |
| |
| auto it = mConfigs.find(*mActiveConfigId); |
| if (it == mConfigs.end()) { |
| ALOGE("%s: failed to find config %" PRId32, __func__, *mActiveConfigId); |
| return HWC3::Error::NoResources; |
| } |
| it->second.setAttribute(DisplayAttribute::VSYNC_PERIOD, |
| 1000 * 1000 * 1000 / refreshRateHz); |
| it->second.setAttribute(DisplayAttribute::WIDTH, width); |
| it->second.setAttribute(DisplayAttribute::HEIGHT, height); |
| it->second.setAttribute(DisplayAttribute::DPI_X, dpiX); |
| it->second.setAttribute(DisplayAttribute::DPI_Y, dpiY); |
| |
| if (edid.has_value()) { |
| mEdid = *edid; |
| } |
| |
| return HWC3::Error::None; |
| } |
| |
| HWC3::Error Display::createLayer(int64_t* outLayerId) { |
| DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId); |
| |
| std::unique_lock<std::recursive_mutex> lock(mStateMutex); |
| |
| auto layer = std::make_unique<Layer>(); |
| |
| const int64_t layerId = layer->getId(); |
| DEBUG_LOG("%s: created layer:%" PRId64, __FUNCTION__, layerId); |
| |
| mLayers.emplace(layerId, std::move(layer)); |
| |
| *outLayerId = layerId; |
| |
| return HWC3::Error::None; |
| } |
| |
| HWC3::Error Display::destroyLayer(int64_t layerId) { |
| DEBUG_LOG("%s: destroy layer:%" PRId64, __FUNCTION__, layerId); |
| |
| std::unique_lock<std::recursive_mutex> lock(mStateMutex); |
| |
| auto it = mLayers.find(layerId); |
| if (it == mLayers.end()) { |
| ALOGE("%s display:%" PRId64 " has no such layer:%." PRId64, __FUNCTION__, |
| mId, layerId); |
| return HWC3::Error::BadLayer; |
| } |
| |
| mOrderedLayers.erase(std::remove_if(mOrderedLayers.begin(), // |
| mOrderedLayers.end(), // |
| [layerId](Layer* layer) { |
| return layer->getId() == layerId; |
| }), |
| mOrderedLayers.end()); |
| |
| mLayers.erase(it); |
| |
| DEBUG_LOG("%s: destroyed layer:%" PRId64, __FUNCTION__, layerId); |
| return HWC3::Error::None; |
| } |
| |
| HWC3::Error Display::getActiveConfig(int32_t* outConfig) { |
| DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId); |
| |
| std::unique_lock<std::recursive_mutex> lock(mStateMutex); |
| |
| if (!mActiveConfigId) { |
| ALOGW("%s: display:%" PRId64 " has no active config.", __FUNCTION__, mId); |
| return HWC3::Error::BadConfig; |
| } |
| |
| *outConfig = *mActiveConfigId; |
| return HWC3::Error::None; |
| } |
| |
| HWC3::Error Display::getDisplayAttribute(int32_t configId, |
| DisplayAttribute attribute, |
| int32_t* outValue) { |
| auto attributeString = toString(attribute); |
| DEBUG_LOG("%s: display:%" PRId64 " attribute:%s", __FUNCTION__, mId, |
| attributeString.c_str()); |
| |
| std::unique_lock<std::recursive_mutex> lock(mStateMutex); |
| |
| auto it = mConfigs.find(configId); |
| if (it == mConfigs.end()) { |
| ALOGW("%s: display:%" PRId64 " bad config:%" PRId32, __FUNCTION__, mId, |
| configId); |
| return HWC3::Error::BadConfig; |
| } |
| |
| const DisplayConfig& config = it->second; |
| *outValue = config.getAttribute(attribute); |
| DEBUG_LOG("%s: display:%" PRId64 " attribute:%s value is %" PRIi32, |
| __FUNCTION__, mId, attributeString.c_str(), *outValue); |
| return HWC3::Error::None; |
| } |
| |
| HWC3::Error Display::getColorModes(std::vector<ColorMode>* outModes) { |
| DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId); |
| |
| std::unique_lock<std::recursive_mutex> lock(mStateMutex); |
| |
| outModes->clear(); |
| outModes->insert(outModes->end(), mColorModes.begin(), mColorModes.end()); |
| |
| return HWC3::Error::None; |
| } |
| |
| HWC3::Error Display::getDisplayCapabilities( |
| std::vector<DisplayCapability>* outCapabilities) { |
| DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId); |
| |
| outCapabilities->clear(); |
| outCapabilities->push_back(DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM); |
| |
| return HWC3::Error::None; |
| } |
| |
| HWC3::Error Display::getDisplayConfigs(std::vector<int32_t>* outConfigIds) { |
| DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId); |
| |
| std::unique_lock<std::recursive_mutex> lock(mStateMutex); |
| |
| outConfigIds->clear(); |
| outConfigIds->reserve(mConfigs.size()); |
| for (const auto& [configId, _] : mConfigs) { |
| outConfigIds->push_back(configId); |
| } |
| |
| return HWC3::Error::None; |
| } |
| |
| HWC3::Error Display::getDisplayConnectionType(DisplayConnectionType* outType) { |
| if (IsCuttlefishFoldable() || IsAutoDevice()) { |
| // Android Auto OS needs to set all displays to INTERNAL since they're used |
| // for the passenger displays. |
| // Workaround to force all displays to INTERNAL for cf_x86_64_foldable. |
| // TODO(b/193568008): Allow configuring internal/external per display. |
| *outType = DisplayConnectionType::INTERNAL; |
| } else { |
| // Other devices default to the first display INTERNAL, others EXTERNAL. |
| *outType = mId == 0 ? DisplayConnectionType::INTERNAL |
| : DisplayConnectionType::EXTERNAL; |
| } |
| return HWC3::Error::None; |
| } |
| |
| HWC3::Error Display::getDisplayIdentificationData( |
| DisplayIdentification* outIdentification) { |
| DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId); |
| |
| if (outIdentification == nullptr) { |
| return HWC3::Error::BadParameter; |
| } |
| |
| outIdentification->port = mId; |
| outIdentification->data = mEdid; |
| |
| return HWC3::Error::None; |
| } |
| |
| HWC3::Error Display::getDisplayName(std::string* outName) { |
| DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId); |
| |
| std::unique_lock<std::recursive_mutex> lock(mStateMutex); |
| |
| *outName = mName; |
| return HWC3::Error::None; |
| } |
| |
| HWC3::Error Display::getDisplayVsyncPeriod(int32_t* outVsyncPeriod) { |
| DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId); |
| |
| std::unique_lock<std::recursive_mutex> lock(mStateMutex); |
| |
| if (!mActiveConfigId) { |
| ALOGE("%s : display:%" PRId64 " no active config", __FUNCTION__, mId); |
| return HWC3::Error::BadConfig; |
| } |
| |
| const auto it = mConfigs.find(*mActiveConfigId); |
| if (it == mConfigs.end()) { |
| ALOGE("%s : display:%" PRId64 " failed to find active config:%" PRId32, |
| __FUNCTION__, mId, *mActiveConfigId); |
| return HWC3::Error::BadConfig; |
| } |
| const DisplayConfig& activeConfig = it->second; |
| |
| *outVsyncPeriod = activeConfig.getAttribute(DisplayAttribute::VSYNC_PERIOD); |
| return HWC3::Error::None; |
| } |
| |
| HWC3::Error Display::getDisplayedContentSample( |
| int64_t /*maxFrames*/, int64_t /*timestamp*/, |
| DisplayContentSample* /*samples*/) { |
| DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId); |
| |
| return HWC3::Error::Unsupported; |
| } |
| |
| HWC3::Error Display::getDisplayedContentSamplingAttributes( |
| DisplayContentSamplingAttributes* /*outAttributes*/) { |
| DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId); |
| |
| return HWC3::Error::Unsupported; |
| } |
| |
| HWC3::Error Display::getDisplayPhysicalOrientation( |
| common::Transform* outOrientation) { |
| DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId); |
| |
| *outOrientation = common::Transform::NONE; |
| |
| return HWC3::Error::None; |
| } |
| |
| HWC3::Error Display::getHdrCapabilities(HdrCapabilities* outCapabilities) { |
| DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId); |
| |
| // No supported types. |
| outCapabilities->types.clear(); |
| |
| return HWC3::Error::None; |
| } |
| |
| HWC3::Error Display::getPerFrameMetadataKeys( |
| std::vector<PerFrameMetadataKey>* outKeys) { |
| DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId); |
| |
| outKeys->clear(); |
| |
| return HWC3::Error::Unsupported; |
| } |
| |
| HWC3::Error Display::getReadbackBufferAttributes( |
| ReadbackBufferAttributes* outAttributes) { |
| DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId); |
| |
| outAttributes->format = common::PixelFormat::RGBA_8888; |
| outAttributes->dataspace = common::Dataspace::UNKNOWN; |
| |
| return HWC3::Error::Unsupported; |
| } |
| |
| HWC3::Error Display::getReadbackBufferFence( |
| ndk::ScopedFileDescriptor* /*outAcquireFence*/) { |
| DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId); |
| |
| return HWC3::Error::Unsupported; |
| } |
| |
| HWC3::Error Display::getRenderIntents(ColorMode mode, |
| std::vector<RenderIntent>* outIntents) { |
| const auto modeString = toString(mode); |
| DEBUG_LOG("%s: display:%" PRId64 "for mode:%s", __FUNCTION__, mId, |
| modeString.c_str()); |
| |
| outIntents->clear(); |
| |
| if (!isValidColorMode(mode)) { |
| DEBUG_LOG("%s: display:%" PRId64 "invalid mode:%s", __FUNCTION__, mId, |
| modeString.c_str()); |
| return HWC3::Error::BadParameter; |
| } |
| |
| outIntents->push_back(RenderIntent::COLORIMETRIC); |
| |
| return HWC3::Error::None; |
| } |
| |
| HWC3::Error Display::getSupportedContentTypes( |
| std::vector<ContentType>* outTypes) { |
| DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId); |
| |
| outTypes->clear(); |
| |
| return HWC3::Error::None; |
| } |
| |
| HWC3::Error Display::getDecorationSupport( |
| std::optional<common::DisplayDecorationSupport>* outSupport) { |
| DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId); |
| |
| outSupport->reset(); |
| |
| return HWC3::Error::Unsupported; |
| } |
| |
| HWC3::Error Display::registerCallback( |
| const std::shared_ptr<IComposerCallback>& callback) { |
| DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId); |
| |
| mVsyncThread.setCallbacks(callback); |
| |
| return HWC3::Error::Unsupported; |
| } |
| |
| HWC3::Error Display::setActiveConfig(int32_t configId) { |
| DEBUG_LOG("%s: display:%" PRId64 " setting active config to %" PRId32, |
| __FUNCTION__, mId, configId); |
| |
| VsyncPeriodChangeConstraints constraints; |
| constraints.desiredTimeNanos = 0; |
| constraints.seamlessRequired = false; |
| |
| VsyncPeriodChangeTimeline timeline; |
| |
| return setActiveConfigWithConstraints(configId, constraints, &timeline); |
| } |
| |
| HWC3::Error Display::setActiveConfigWithConstraints( |
| int32_t configId, const VsyncPeriodChangeConstraints& constraints, |
| VsyncPeriodChangeTimeline* outTimeline) { |
| DEBUG_LOG("%s: display:%" PRId64 " config:%" PRId32, __FUNCTION__, mId, |
| configId); |
| |
| if (outTimeline == nullptr) { |
| return HWC3::Error::BadParameter; |
| } |
| |
| std::unique_lock<std::recursive_mutex> lock(mStateMutex); |
| |
| if (mActiveConfigId == configId) { |
| return HWC3::Error::None; |
| } |
| |
| DisplayConfig* newConfig = getConfig(configId); |
| if (newConfig == nullptr) { |
| ALOGE("%s: display:%" PRId64 " bad config:%" PRId32, __FUNCTION__, mId, |
| configId); |
| return HWC3::Error::BadConfig; |
| } |
| |
| if (constraints.seamlessRequired) { |
| if (mActiveConfigId) { |
| DisplayConfig* oldConfig = getConfig(*mActiveConfigId); |
| if (oldConfig == nullptr) { |
| ALOGE("%s: display:%" PRId64 " missing config:%" PRId32, __FUNCTION__, |
| mId, *mActiveConfigId); |
| return HWC3::Error::NoResources; |
| } |
| |
| const int32_t newConfigGroup = newConfig->getConfigGroup(); |
| const int32_t oldConfigGroup = oldConfig->getConfigGroup(); |
| if (newConfigGroup != oldConfigGroup) { |
| DEBUG_LOG("%s: display:%" PRId64 " config:%" PRId32 |
| " seamless not supported between different config groups " |
| "old:%d vs new:%d", |
| __FUNCTION__, mId, configId, oldConfigGroup, newConfigGroup); |
| return HWC3::Error::SeamlessNotAllowed; |
| } |
| } |
| } |
| |
| mActiveConfigId = configId; |
| |
| if (mComposer == nullptr) { |
| ALOGE("%s: display:%" PRId64 " missing composer", __FUNCTION__, mId); |
| return HWC3::Error::NoResources; |
| } |
| |
| HWC3::Error error = mComposer->onActiveConfigChange(this); |
| if (error != HWC3::Error::None) { |
| ALOGE("%s: display:%" PRId64 " composer failed to handle config change", |
| __FUNCTION__, mId); |
| return error; |
| } |
| |
| int32_t vsyncPeriod; |
| error = getDisplayVsyncPeriod(&vsyncPeriod); |
| if (error != HWC3::Error::None) { |
| ALOGE("%s: display:%" PRId64 " composer failed to handle config change", |
| __FUNCTION__, mId); |
| return error; |
| } |
| |
| return mVsyncThread.scheduleVsyncUpdate(vsyncPeriod, constraints, |
| outTimeline); |
| } |
| |
| std::optional<int32_t> Display::getBootConfigId() { |
| DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId); |
| |
| std::unique_lock<std::recursive_mutex> lock(mStateMutex); |
| |
| std::string val; |
| HWC3::Error error = Device::getInstance().getPersistentKeyValue( |
| std::to_string(mId), "", &val); |
| if (error != HWC3::Error::None) { |
| ALOGE("%s: display:%" PRId64 " failed to get persistent boot config", |
| __FUNCTION__, mId); |
| return std::nullopt; |
| } |
| |
| if (val.empty()) { |
| return std::nullopt; |
| } |
| |
| int32_t configId = 0; |
| if (!::android::base::ParseInt(val, &configId)) { |
| ALOGE("%s: display:%" PRId64 |
| " failed to parse persistent boot config from: %s", |
| __FUNCTION__, mId, val.c_str()); |
| return std::nullopt; |
| } |
| |
| if (!hasConfig(configId)) { |
| ALOGE("%s: display:%" PRId64 " invalid persistent boot config:%" PRId32, |
| __FUNCTION__, mId, configId); |
| return std::nullopt; |
| } |
| |
| return configId; |
| } |
| |
| HWC3::Error Display::setBootConfig(int32_t configId) { |
| DEBUG_LOG("%s: display:%" PRId64 " config:%" PRId32, __FUNCTION__, mId, |
| configId); |
| |
| std::unique_lock<std::recursive_mutex> lock(mStateMutex); |
| |
| DisplayConfig* newConfig = getConfig(configId); |
| if (newConfig == nullptr) { |
| ALOGE("%s: display:%" PRId64 " bad config:%" PRId32, __FUNCTION__, mId, |
| configId); |
| return HWC3::Error::BadConfig; |
| } |
| |
| const std::string key = std::to_string(mId); |
| const std::string val = std::to_string(configId); |
| HWC3::Error error = Device::getInstance().setPersistentKeyValue(key, val); |
| if (error != HWC3::Error::None) { |
| ALOGE("%s: display:%" PRId64 " failed to save persistent boot config", |
| __FUNCTION__, mId); |
| return error; |
| } |
| |
| return HWC3::Error::None; |
| } |
| |
| HWC3::Error Display::clearBootConfig() { |
| DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId); |
| |
| std::unique_lock<std::recursive_mutex> lock(mStateMutex); |
| |
| const std::string key = std::to_string(mId); |
| const std::string val = ""; |
| HWC3::Error error = Device::getInstance().setPersistentKeyValue(key, val); |
| if (error != HWC3::Error::None) { |
| ALOGE("%s: display:%" PRId64 " failed to save persistent boot config", |
| __FUNCTION__, mId); |
| return error; |
| } |
| |
| return HWC3::Error::None; |
| } |
| |
| HWC3::Error Display::getPreferredBootConfig(int32_t* outConfigId) { |
| DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId); |
| |
| std::unique_lock<std::recursive_mutex> lock(mStateMutex); |
| |
| std::vector<int32_t> configIds; |
| for (const auto [configId, _] : mConfigs) { |
| configIds.push_back(configId); |
| } |
| *outConfigId = *std::min_element(configIds.begin(), configIds.end()); |
| |
| return HWC3::Error::None; |
| } |
| |
| HWC3::Error Display::setAutoLowLatencyMode(bool /*on*/) { |
| DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId); |
| |
| return HWC3::Error::Unsupported; |
| } |
| |
| HWC3::Error Display::setColorMode(ColorMode mode, RenderIntent intent) { |
| const std::string modeString = toString(mode); |
| const std::string intentString = toString(intent); |
| DEBUG_LOG("%s: display:%" PRId64 " setting color mode:%s intent:%s", |
| __FUNCTION__, mId, modeString.c_str(), intentString.c_str()); |
| |
| if (!isValidColorMode(mode)) { |
| ALOGE("%s: display:%" PRId64 " invalid color mode:%s", __FUNCTION__, mId, |
| modeString.c_str()); |
| return HWC3::Error::BadParameter; |
| } |
| |
| if (!isValidRenderIntent(intent)) { |
| ALOGE("%s: display:%" PRId64 " invalid intent:%s", __FUNCTION__, mId, |
| intentString.c_str()); |
| return HWC3::Error::BadParameter; |
| } |
| |
| std::unique_lock<std::recursive_mutex> lock(mStateMutex); |
| |
| if (mColorModes.count(mode) == 0) { |
| ALOGE("%s: display %" PRId64 " mode %s not supported", __FUNCTION__, mId, |
| modeString.c_str()); |
| return HWC3::Error::Unsupported; |
| } |
| |
| mActiveColorMode = mode; |
| return HWC3::Error::None; |
| } |
| |
| HWC3::Error Display::setContentType(ContentType contentType) { |
| auto contentTypeString = toString(contentType); |
| DEBUG_LOG("%s: display:%" PRId64 " content type:%s", __FUNCTION__, mId, |
| contentTypeString.c_str()); |
| |
| if (contentType != ContentType::NONE) { |
| return HWC3::Error::Unsupported; |
| } |
| |
| return HWC3::Error::None; |
| } |
| |
| HWC3::Error Display::setDisplayedContentSamplingEnabled( |
| bool /*enable*/, FormatColorComponent /*componentMask*/, |
| int64_t /*maxFrames*/) { |
| DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId); |
| |
| return HWC3::Error::Unsupported; |
| } |
| |
| HWC3::Error Display::setPowerMode(PowerMode mode) { |
| auto modeString = toString(mode); |
| DEBUG_LOG("%s: display:%" PRId64 " to mode:%s", __FUNCTION__, mId, |
| modeString.c_str()); |
| |
| if (!isValidPowerMode(mode)) { |
| ALOGE("%s: display:%" PRId64 " invalid mode:%s", __FUNCTION__, mId, |
| modeString.c_str()); |
| return HWC3::Error::BadParameter; |
| } |
| |
| if (mode == PowerMode::DOZE || mode == PowerMode::DOZE_SUSPEND || |
| mode == PowerMode::ON_SUSPEND) { |
| ALOGE("%s display %" PRId64 " mode:%s not supported", __FUNCTION__, mId, |
| modeString.c_str()); |
| return HWC3::Error::Unsupported; |
| } |
| |
| std::unique_lock<std::recursive_mutex> lock(mStateMutex); |
| |
| if (IsCuttlefish()) { |
| if (int fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC); fd != -1) { |
| std::ostringstream stream; |
| stream << "VIRTUAL_DEVICE_DISPLAY_POWER_MODE_CHANGED display=" << mId |
| << " mode=" << modeString; |
| std::string message = stream.str(); |
| write(fd, message.c_str(), message.length()); |
| close(fd); |
| } |
| } |
| |
| mPowerMode = mode; |
| return HWC3::Error::None; |
| } |
| |
| HWC3::Error Display::setReadbackBuffer(const buffer_handle_t buffer, |
| const ndk::ScopedFileDescriptor& fence) { |
| DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId); |
| |
| mReadbackBuffer.set(buffer, fence); |
| |
| return HWC3::Error::Unsupported; |
| } |
| |
| HWC3::Error Display::setVsyncEnabled(bool enabled) { |
| DEBUG_LOG("%s: display:%" PRId64 " setting vsync %s", __FUNCTION__, mId, |
| (enabled ? "on" : "off")); |
| |
| std::unique_lock<std::recursive_mutex> lock(mStateMutex); |
| |
| return mVsyncThread.setVsyncEnabled(enabled); |
| } |
| |
| HWC3::Error Display::setIdleTimerEnabled(int32_t timeoutMs) { |
| DEBUG_LOG("%s: display:%" PRId64 " timeout:%" PRId32, __FUNCTION__, mId, |
| timeoutMs); |
| |
| (void)timeoutMs; |
| |
| return HWC3::Error::Unsupported; |
| } |
| |
| HWC3::Error Display::setColorTransform( |
| const std::vector<float>& transformMatrix) { |
| DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId); |
| |
| if (transformMatrix.size() < 16) { |
| ALOGE("%s: display:%" PRId64 " has non 4x4 matrix, size:%zu", __FUNCTION__, |
| mId, transformMatrix.size()); |
| return HWC3::Error::BadParameter; |
| } |
| |
| std::unique_lock<std::recursive_mutex> lock(mStateMutex); |
| |
| auto& colorTransform = mColorTransform.emplace(); |
| std::copy_n(transformMatrix.data(), colorTransform.size(), |
| colorTransform.begin()); |
| |
| return HWC3::Error::None; |
| } |
| |
| HWC3::Error Display::setBrightness(float brightness) { |
| DEBUG_LOG("%s: display:%" PRId64 " brightness:%f", __FUNCTION__, mId, |
| brightness); |
| |
| if (brightness < 0.0f) { |
| ALOGE("%s: display:%" PRId64 " invalid brightness:%f", __FUNCTION__, mId, |
| brightness); |
| return HWC3::Error::BadParameter; |
| } |
| |
| return HWC3::Error::Unsupported; |
| } |
| |
| HWC3::Error Display::setClientTarget( |
| buffer_handle_t buffer, const ndk::ScopedFileDescriptor& fence, |
| common::Dataspace /*dataspace*/, |
| const std::vector<common::Rect>& /*damage*/) { |
| DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId); |
| |
| std::unique_lock<std::recursive_mutex> lock(mStateMutex); |
| |
| mClientTarget.set(buffer, fence); |
| |
| mComposer->onDisplayClientTargetSet(this); |
| return HWC3::Error::None; |
| } |
| |
| HWC3::Error Display::setOutputBuffer( |
| buffer_handle_t /*buffer*/, const ndk::ScopedFileDescriptor& /*fence*/) { |
| DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId); |
| |
| // TODO: for virtual display |
| return HWC3::Error::None; |
| } |
| |
| HWC3::Error Display::setExpectedPresentTime( |
| const std::optional<ClockMonotonicTimestamp>& expectedPresentTime) { |
| DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId); |
| |
| if (!expectedPresentTime.has_value()) { |
| return HWC3::Error::None; |
| } |
| |
| std::unique_lock<std::recursive_mutex> lock(mStateMutex); |
| |
| mExpectedPresentTime.emplace( |
| asTimePoint(expectedPresentTime->timestampNanos)); |
| |
| return HWC3::Error::None; |
| } |
| |
| HWC3::Error Display::validate(DisplayChanges* outChanges) { |
| ATRACE_CALL(); |
| |
| DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId); |
| |
| std::unique_lock<std::recursive_mutex> lock(mStateMutex); |
| |
| mPendingChanges.reset(); |
| |
| mOrderedLayers.clear(); |
| mOrderedLayers.reserve(mLayers.size()); |
| for (auto& [_, layerPtr] : mLayers) { |
| mOrderedLayers.push_back(layerPtr.get()); |
| } |
| std::sort(mOrderedLayers.begin(), mOrderedLayers.end(), |
| [](const Layer* layerA, const Layer* layerB) { |
| const auto zA = layerA->getZOrder(); |
| const auto zB = layerB->getZOrder(); |
| if (zA != zB) { |
| return zA < zB; |
| } |
| return layerA->getId() < layerB->getId(); |
| }); |
| |
| if (mComposer == nullptr) { |
| ALOGE("%s: display:%" PRId64 " missing composer", __FUNCTION__, mId); |
| return HWC3::Error::NoResources; |
| } |
| |
| HWC3::Error error = mComposer->validateDisplay(this, &mPendingChanges); |
| if (error != HWC3::Error::None) { |
| ALOGE("%s: display:%" PRId64 " failed to validate", __FUNCTION__, mId); |
| return error; |
| } |
| |
| if (mPendingChanges.hasAnyChanges()) { |
| mPresentFlowState = PresentFlowState::WAITING_FOR_ACCEPT; |
| DEBUG_LOG("%s: display:%" PRId64 " now WAITING_FOR_ACCEPT", __FUNCTION__, |
| mId); |
| } else { |
| mPresentFlowState = PresentFlowState::WAITING_FOR_PRESENT; |
| DEBUG_LOG("%s: display:%" PRId64 " now WAITING_FOR_PRESENT", __FUNCTION__, |
| mId); |
| } |
| |
| *outChanges = mPendingChanges; |
| return HWC3::Error::None; |
| } |
| |
| HWC3::Error Display::acceptChanges() { |
| DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId); |
| |
| std::unique_lock<std::recursive_mutex> lock(mStateMutex); |
| |
| switch (mPresentFlowState) { |
| case PresentFlowState::WAITING_FOR_VALIDATE: { |
| ALOGE("%s: display %" PRId64 " failed, not validated", __FUNCTION__, mId); |
| return HWC3::Error::NotValidated; |
| } |
| case PresentFlowState::WAITING_FOR_ACCEPT: |
| case PresentFlowState::WAITING_FOR_PRESENT: { |
| break; |
| } |
| } |
| |
| if (mPendingChanges.compositionChanges) { |
| const ChangedCompositionTypes& compositionChanges = |
| *mPendingChanges.compositionChanges; |
| for (const ChangedCompositionLayer& compositionChange : |
| compositionChanges.layers) { |
| const auto layerId = compositionChange.layer; |
| const auto layerComposition = compositionChange.composition; |
| auto* layer = getLayer(layerId); |
| if (layer == nullptr) { |
| ALOGE("%s: display:%" PRId64 " layer:%" PRId64 |
| " dropped before acceptChanges()?", |
| __FUNCTION__, mId, layerId); |
| continue; |
| } |
| |
| layer->setCompositionType(layerComposition); |
| } |
| } |
| mPendingChanges.reset(); |
| |
| mPresentFlowState = PresentFlowState::WAITING_FOR_PRESENT; |
| DEBUG_LOG("%s: display:%" PRId64 " now WAITING_FOR_PRESENT", __FUNCTION__, |
| mId); |
| |
| return HWC3::Error::None; |
| } |
| |
| HWC3::Error Display::present( |
| ::android::base::unique_fd* outDisplayFence, |
| std::unordered_map<int64_t, ::android::base::unique_fd>* outLayerFences) { |
| ATRACE_CALL(); |
| |
| DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId); |
| |
| outDisplayFence->reset(); |
| outLayerFences->clear(); |
| |
| std::unique_lock<std::recursive_mutex> lock(mStateMutex); |
| |
| switch (mPresentFlowState) { |
| case PresentFlowState::WAITING_FOR_VALIDATE: { |
| ALOGE("%s: display %" PRId64 " failed, not validated", __FUNCTION__, mId); |
| return HWC3::Error::NotValidated; |
| } |
| case PresentFlowState::WAITING_FOR_ACCEPT: { |
| ALOGE("%s: display %" PRId64 " failed, changes not accepted", |
| __FUNCTION__, mId); |
| return HWC3::Error::NotValidated; |
| } |
| case PresentFlowState::WAITING_FOR_PRESENT: { |
| break; |
| } |
| } |
| mPresentFlowState = PresentFlowState::WAITING_FOR_VALIDATE; |
| DEBUG_LOG("%s: display:%" PRId64 " now WAITING_FOR_VALIDATE", __FUNCTION__, |
| mId); |
| |
| if (mComposer == nullptr) { |
| ALOGE("%s: display:%" PRId64 " missing composer", __FUNCTION__, mId); |
| return HWC3::Error::NoResources; |
| } |
| |
| return mComposer->presentDisplay(this, outDisplayFence, outLayerFences); |
| } |
| |
| bool Display::hasConfig(int32_t configId) const { |
| return mConfigs.find(configId) != mConfigs.end(); |
| } |
| |
| DisplayConfig* Display::getConfig(int32_t configId) { |
| auto it = mConfigs.find(configId); |
| if (it != mConfigs.end()) { |
| return &it->second; |
| } |
| return nullptr; |
| } |
| |
| HWC3::Error Display::setEdid(std::vector<uint8_t> edid) { |
| DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId); |
| |
| mEdid = edid; |
| return HWC3::Error::None; |
| } |
| |
| void Display::setLegacyEdid() { |
| // thess EDIDs are carefully generated according to the EDID spec version 1.3, |
| // more info can be found from the following file: |
| // frameworks/native/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp |
| // approved pnp ids can be found here: https://uefi.org/pnp_id_list |
| // pnp id: GGL, name: EMU_display_0, last byte is checksum |
| // display id is local:8141603649153536 |
| static constexpr const std::array<uint8_t, 128> kEdid0 = { |
| 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1c, 0xec, 0x01, 0x00, |
| 0x01, 0x00, 0x00, 0x00, 0x1b, 0x10, 0x01, 0x03, 0x80, 0x50, 0x2d, 0x78, |
| 0x0a, 0x0d, 0xc9, 0xa0, 0x57, 0x47, 0x98, 0x27, 0x12, 0x48, 0x4c, 0x00, |
| 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, |
| 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, |
| 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0xfc, 0x00, 0x45, 0x4d, 0x55, 0x5f, 0x64, 0x69, 0x73, |
| 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x30, 0x00, 0x4b}; |
| |
| // pnp id: GGL, name: EMU_display_1 |
| // display id is local:8140900251843329 |
| static constexpr const std::array<uint8_t, 128> kEdid1 = { |
| 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1c, 0xec, 0x01, 0x00, |
| 0x01, 0x00, 0x00, 0x00, 0x1b, 0x10, 0x01, 0x03, 0x80, 0x50, 0x2d, 0x78, |
| 0x0a, 0x0d, 0xc9, 0xa0, 0x57, 0x47, 0x98, 0x27, 0x12, 0x48, 0x4c, 0x00, |
| 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, |
| 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, |
| 0x2d, 0x40, 0x58, 0x2c, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0xfc, 0x00, 0x45, 0x4d, 0x55, 0x5f, 0x64, 0x69, 0x73, |
| 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x31, 0x00, 0x3b}; |
| |
| // pnp id: GGL, name: EMU_display_2 |
| // display id is local:8140940453066754 |
| static constexpr const std::array<uint8_t, 128> kEdid2 = { |
| 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1c, 0xec, 0x01, 0x00, |
| 0x01, 0x00, 0x00, 0x00, 0x1b, 0x10, 0x01, 0x03, 0x80, 0x50, 0x2d, 0x78, |
| 0x0a, 0x0d, 0xc9, 0xa0, 0x57, 0x47, 0x98, 0x27, 0x12, 0x48, 0x4c, 0x00, |
| 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, |
| 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, |
| 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0xfc, 0x00, 0x45, 0x4d, 0x55, 0x5f, 0x64, 0x69, 0x73, |
| 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x32, 0x00, 0x49}; |
| |
| mEdid.clear(); |
| switch (mId) { |
| case 0: { |
| mEdid.insert(mEdid.end(), kEdid0.begin(), kEdid0.end()); |
| break; |
| } |
| case 1: { |
| mEdid.insert(mEdid.end(), kEdid1.begin(), kEdid1.end()); |
| break; |
| } |
| case 2: { |
| mEdid.insert(mEdid.end(), kEdid2.begin(), kEdid2.end()); |
| break; |
| } |
| default: { |
| mEdid.insert(mEdid.end(), kEdid2.begin(), kEdid2.end()); |
| const uint32_t size = mEdid.size(); |
| // Update the name to EMU_display_<mID> |
| mEdid[size - 3] = '0' + (uint8_t)mId; |
| // Update the checksum byte |
| uint8_t checksum = -(uint8_t)std::accumulate( |
| mEdid.data(), mEdid.data() + size - 1, static_cast<uint8_t>(0)); |
| mEdid[size - 1] = checksum; |
| break; |
| } |
| } |
| } |
| |
| Layer* Display::getLayer(int64_t layerId) { |
| auto it = mLayers.find(layerId); |
| if (it == mLayers.end()) { |
| ALOGE("%s Unknown layer:%" PRId64, __FUNCTION__, layerId); |
| return nullptr; |
| } |
| |
| return it->second.get(); |
| } |
| |
| buffer_handle_t Display::waitAndGetClientTargetBuffer() { |
| DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId); |
| |
| ::android::base::unique_fd fence = mClientTarget.getFence(); |
| if (fence.ok()) { |
| int err = sync_wait(fence.get(), 3000); |
| if (err < 0 && errno == ETIME) { |
| ALOGE("%s waited on fence %" PRId32 " for 3000 ms", __FUNCTION__, |
| fence.get()); |
| } |
| } |
| |
| return mClientTarget.getBuffer(); |
| } |
| |
| } // namespace aidl::android::hardware::graphics::composer3::impl |