1051 lines
32 KiB
C++
1051 lines
32 KiB
C++
/*
|
|
* 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()) {
|
|
// 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
|