diff options
-rw-r--r-- | system/hwc2/Android.mk | 4 | ||||
-rw-r--r-- | system/hwc2/Common.cpp | 6 | ||||
-rw-r--r-- | system/hwc2/Common.h | 1 | ||||
-rw-r--r-- | system/hwc2/Device.cpp | 123 | ||||
-rw-r--r-- | system/hwc2/Device.h | 5 | ||||
-rw-r--r-- | system/hwc2/Display.cpp | 288 | ||||
-rw-r--r-- | system/hwc2/Display.h | 74 | ||||
-rw-r--r-- | system/hwc2/DisplayConfig.cpp | 124 | ||||
-rw-r--r-- | system/hwc2/DisplayConfig.h | 84 | ||||
-rw-r--r-- | system/hwc2/DisplayFinder.cpp | 120 | ||||
-rw-r--r-- | system/hwc2/DisplayFinder.h | 14 | ||||
-rw-r--r-- | system/hwc2/DrmPresenter.h | 2 | ||||
-rw-r--r-- | system/hwc2/HostComposer.cpp | 8 | ||||
-rw-r--r-- | system/hwc2/VsyncThread.cpp | 193 | ||||
-rw-r--r-- | system/hwc2/VsyncThread.h | 86 |
15 files changed, 810 insertions, 322 deletions
diff --git a/system/hwc2/Android.mk b/system/hwc2/Android.mk index 4f1be502..e9090df4 100644 --- a/system/hwc2/Android.mk +++ b/system/hwc2/Android.mk @@ -61,6 +61,7 @@ emulator_hwcomposer2_src_files := \ Common.cpp \ Device.cpp \ Display.cpp \ + DisplayConfig.cpp \ DisplayFinder.cpp \ Drm.cpp \ DrmPresenter.cpp \ @@ -69,6 +70,7 @@ emulator_hwcomposer2_src_files := \ HostComposer.cpp \ HostUtils.cpp \ Layer.cpp \ + VsyncThread.cpp \ include $(CLEAR_VARS) @@ -85,7 +87,7 @@ LOCAL_MODULE_RELATIVE_PATH := $(emulator_hwcomposer_relative_path) LOCAL_MODULE := hwcomposer.ranchu LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 LOCAL_LICENSE_CONDITIONS := notice -LOCAL_VINTF_FRAGMENTS := android.hardware.graphics.composer@2.3.xml +LOCAL_VINTF_FRAGMENTS := android.hardware.graphics.composer@2.4.xml LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../LICENSE LOCAL_MODULE_TAGS := optional diff --git a/system/hwc2/Common.cpp b/system/hwc2/Common.cpp index b323b665..19217411 100644 --- a/system/hwc2/Common.cpp +++ b/system/hwc2/Common.cpp @@ -21,3 +21,9 @@ bool IsCuttlefish() { return android::base::GetProperty("ro.product.board", "") == "cutf"; } + +bool IsCuttlefishFoldable() { + return IsCuttlefish() && + android::base::GetProperty("ro.product.name", "").find("foldable") != + std::string::npos; +}
\ No newline at end of file diff --git a/system/hwc2/Common.h b/system/hwc2/Common.h index 770c87f1..e8432135 100644 --- a/system/hwc2/Common.h +++ b/system/hwc2/Common.h @@ -40,5 +40,6 @@ #undef HWC2_USE_CPP11 bool IsCuttlefish(); +bool IsCuttlefishFoldable(); #endif diff --git a/system/hwc2/Device.cpp b/system/hwc2/Device.cpp index 1f06f439..eaf8bbad 100644 --- a/system/hwc2/Device.cpp +++ b/system/hwc2/Device.cpp @@ -105,9 +105,8 @@ HWC2::Error Device::createDisplays() { return error; } - for (const auto& iter: displays) { - - error = createDisplay(iter.configs, iter.activeConfigId); + for (const auto& iter : displays) { + error = createDisplay(iter.displayId, iter.activeConfigId, iter.configs); if (error != HWC2::Error::None) { ALOGE("%s failed to create display from config", __FUNCTION__); return error; @@ -117,8 +116,9 @@ HWC2::Error Device::createDisplays() { return HWC2::Error::None; } -HWC2::Error Device::createDisplay(const std::vector<DisplayConfig>& configs, - int activeConfig) { +HWC2::Error Device::createDisplay(hwc2_display_t displayId, + hwc2_config_t activeConfigId, + const std::vector<DisplayConfig>& configs) { DEBUG_LOG("%s", __FUNCTION__); if (!mComposer) { @@ -126,23 +126,21 @@ HWC2::Error Device::createDisplay(const std::vector<DisplayConfig>& configs, return HWC2::Error::NoResources; } - uint32_t displayId = configs[0].id; - auto display = std::make_unique<Display>(*this, mComposer.get(), - displayId); + auto display = std::make_unique<Display>(mComposer.get(), displayId); if (display == nullptr) { ALOGE("%s failed to allocate display", __FUNCTION__); return HWC2::Error::NoResources; } - HWC2::Error error = display->init(configs, activeConfig); + HWC2::Error error = display->init(configs, activeConfigId); if (error != HWC2::Error::None) { - ALOGE("%s failed to initialize display:%" PRIu32, __FUNCTION__, displayId); + ALOGE("%s failed to initialize display:%" PRIu64, __FUNCTION__, displayId); return error; } error = mComposer->onDisplayCreate(display.get()); if (error != HWC2::Error::None) { - ALOGE("%s failed to register display:%" PRIu32 " with composer", + ALOGE("%s failed to register display:%" PRIu64 " with composer", __FUNCTION__, displayId); return error; } @@ -354,6 +352,33 @@ hwc2_function_pointer_t Device::getFunction(int32_t desc) { return asFP<HWC2_PFN_SET_DISPLAY_BRIGHTNESS>( displayHook<decltype(&Display::setDisplayBrightness), &Display::setDisplayBrightness, float>); + case HWC2::FunctionDescriptor::GetDisplayVsyncPeriod: + return asFP<HWC2_PFN_GET_DISPLAY_VSYNC_PERIOD>( + displayHook<decltype(&Display::getDisplayVsyncPeriod), + &Display::getDisplayVsyncPeriod, hwc2_vsync_period_t*>); + case HWC2::FunctionDescriptor::SetActiveConfigWithConstraints: + return asFP<HWC2_PFN_SET_ACTIVE_CONFIG_WITH_CONSTRAINTS>( + displayHook<decltype(&Display::setActiveConfigWithConstraints), + &Display::setActiveConfigWithConstraints, hwc2_config_t, + hwc_vsync_period_change_constraints_t*, + hwc_vsync_period_change_timeline_t*>); + case HWC2::FunctionDescriptor::GetDisplayConnectionType: + return asFP<HWC2_PFN_GET_DISPLAY_CONNECTION_TYPE>( + displayHook<decltype(&Display::getDisplayConnectionType), + &Display::getDisplayConnectionType, uint32_t*>); + case HWC2::FunctionDescriptor::SetAutoLowLatencyMode: + return asFP<HWC2_PFN_SET_AUTO_LOW_LATENCY_MODE>( + displayHook<decltype(&Display::setAutoLowLatencyMode), + &Display::setAutoLowLatencyMode, bool>); + case HWC2::FunctionDescriptor::GetSupportedContentTypes: + return asFP<HWC2_PFN_GET_SUPPORTED_CONTENT_TYPES>( + displayHook<decltype(&Display::getSupportedContentTypes), + &Display::getSupportedContentTypes, uint32_t*, + uint32_t*>); + case HWC2::FunctionDescriptor::SetContentType: + return asFP<HWC2_PFN_SET_CONTENT_TYPE>( + displayHook<decltype(&Display::setContentType), + &Display::setContentType, int32_t>); // Layer functions case HWC2::FunctionDescriptor::SetCursorPosition: @@ -455,64 +480,50 @@ uint32_t Device::getMaxVirtualDisplayCount() { return 0; } -static bool IsHandledCallback(HWC2::Callback descriptor) { - switch (descriptor) { - case HWC2::Callback::Hotplug: { - return true; - } - case HWC2::Callback::Refresh: { - return true; - } - case HWC2::Callback::Vsync: { - return true; - } - case HWC2::Callback::Vsync_2_4: { - return false; - } - case HWC2::Callback::VsyncPeriodTimingChanged: { - return false; - } - case HWC2::Callback::Invalid: { - return false; - } - case HWC2::Callback::SeamlessPossible: { - return false; - } - } - return false; -} - HWC2::Error Device::registerCallback(int32_t desc, hwc2_callback_data_t callbackData, - hwc2_function_pointer_t pointer) { + hwc2_function_pointer_t callbackPointer) { const auto callbackType = static_cast<HWC2::Callback>(desc); const auto callbackTypeString = to_string(callbackType); DEBUG_LOG("%s callback %s", __FUNCTION__, callbackTypeString.c_str()); - if (!IsHandledCallback(callbackType)) { - ALOGE("%s unhandled callback: %s", __FUNCTION__, - callbackTypeString.c_str()); - return HWC2::Error::BadParameter; - } - std::unique_lock<std::mutex> lock(mStateMutex); - if (pointer != nullptr) { - mCallbacks[callbackType] = {callbackData, pointer}; + if (callbackPointer != nullptr) { + mCallbacks[callbackType] = {callbackData, callbackPointer}; } else { mCallbacks.erase(callbackType); - return HWC2::Error::None; } - if (callbackType == HWC2::Callback::Hotplug) { + if (callbackType == HWC2::Callback::Vsync) { + auto callback = reinterpret_cast<HWC2_PFN_VSYNC>(callbackPointer); + for (const auto& [displayId, display] : mDisplays) { + display->setVsyncCallback(callback, callbackData); + } + } else if (callbackType == HWC2::Callback::Vsync_2_4) { + auto callback = reinterpret_cast<HWC2_PFN_VSYNC_2_4>(callbackPointer); + for (const auto& [displayId, display] : mDisplays) { + display->setVsync24Callback(callback, callbackData); + } + } else if (callbackType == HWC2::Callback::Hotplug) { + if (callbackPointer == nullptr) { + return HWC2::Error::None; + } + // Callback without the state lock held lock.unlock(); - auto hotplug = reinterpret_cast<HWC2_PFN_HOTPLUG>(pointer); + auto hotplug = reinterpret_cast<HWC2_PFN_HOTPLUG>(callbackPointer); auto hotplugConnect = static_cast<int32_t>(HWC2::Connection::Connected); for (const auto& [displayId, display] : mDisplays) { ALOGI("%s hotplug connecting display:%" PRIu64, __FUNCTION__, displayId); hotplug(callbackData, displayId, hotplugConnect); } + } else if (callbackType == HWC2::Callback::Refresh) { + // Not used + } else { + ALOGE("%s unhandled callback: %s", __FUNCTION__, + callbackTypeString.c_str()); + return HWC2::Error::BadParameter; } return HWC2::Error::None; @@ -539,11 +550,13 @@ bool Device::handleHotplug(bool connected, uint32_t id, uint32_t width, display->unlock(); } if (connected) { - std::vector<DisplayConfig> config; - config.push_back({static_cast<int>(id), static_cast<int>(width), + hwc2_display_t displayId = static_cast<hwc2_display_t>(id); + hwc2_config_t configId = static_cast<hwc2_config_t>(id); + std::vector<DisplayConfig> configs = { + DisplayConfig(configId, static_cast<int>(width), static_cast<int>(height), static_cast<int>(dpiX), - static_cast<int>(dpiY), static_cast<int>(refreshRate)}); - createDisplay(config, 0); + static_cast<int>(dpiY), static_cast<int>(refreshRate))}; + createDisplay(displayId, configId, configs); ALOGD("callback hotplugConnect display %" PRIu32 " width %" PRIu32 " height %" PRIu32 " dpiX %" PRIu32 " dpiY %" PRIu32 "fps %" PRIu32, id, width, height, dpiX, dpiY, refreshRate); @@ -598,7 +611,7 @@ static struct hw_module_methods_t hwc2_module_methods = { hw_module_t HAL_MODULE_INFO_SYM = { .tag = HARDWARE_MODULE_TAG, .version_major = 2, - .version_minor = 3, + .version_minor = 4, .id = HWC_HARDWARE_MODULE_ID, .name = "goldfish HWC2 module", .author = "The Android Open Source Project", diff --git a/system/hwc2/Device.h b/system/hwc2/Device.h index b49d1a0f..812d5e9b 100644 --- a/system/hwc2/Device.h +++ b/system/hwc2/Device.h @@ -53,8 +53,9 @@ class Device : public hwc2_device_t { HWC2::Error createDisplays(); - HWC2::Error createDisplay(const std::vector<DisplayConfig>& configs, - int activeConfig); + HWC2::Error createDisplay(hwc2_display_t displayId, + hwc2_config_t activeConfigId, + const std::vector<DisplayConfig>& configs); Display* getDisplay(hwc2_display_t displayId); diff --git a/system/hwc2/Display.cpp b/system/hwc2/Display.cpp index b373ca9c..9d754cba 100644 --- a/system/hwc2/Display.cpp +++ b/system/hwc2/Display.cpp @@ -30,8 +30,6 @@ namespace { using android::hardware::graphics::common::V1_0::ColorTransform; -std::atomic<hwc2_config_t> sNextConfigId{0}; - bool IsValidColorMode(android_color_mode_t mode) { switch (mode) { case HAL_COLOR_MODE_NATIVE: // Fall-through @@ -64,46 +62,37 @@ bool isValidPowerMode(HWC2::PowerMode mode) { } // namespace -Display::Display(Device& device, Composer* composer, hwc2_display_t id) - : mDevice(device), - mComposer(composer), - mId(id), - mVsyncThread(new VsyncThread(*this)) {} +Display::Display(Composer* composer, hwc2_display_t id) + : mComposer(composer), mId(id), mVsyncThread(new VsyncThread(id)) {} Display::~Display() {} HWC2::Error Display::init(const std::vector<DisplayConfig>& configs, - int activeConfig, + hwc2_config_t activeConfigId, const std::optional<std::vector<uint8_t>>& edid) { - ALOGD("%s initializing display:%" PRIu64 - " width:%d height:%d dpiX:%d dpiY:%d refreshRateHz:%d", - __FUNCTION__, mId, configs[activeConfig].width, - configs[activeConfig].height, - configs[activeConfig].dpiX, - configs[activeConfig].dpiY, - configs[activeConfig].refreshRateHz); - std::unique_lock<std::recursive_mutex> lock(mStateMutex); - mVsyncPeriod = 1000 * 1000 * 1000 / configs[activeConfig].refreshRateHz; - mVsyncThread->run("", ANDROID_PRIORITY_URGENT_DISPLAY); - - for (hwc2_config_t id = 0; id < configs.size(); id++) { - Config config(id); - config.setAttribute(HWC2::Attribute::VsyncPeriod, - 1000 * 1000 * 1000 / configs[id].refreshRateHz); - config.setAttribute(HWC2::Attribute::Width, configs[id].width); - config.setAttribute(HWC2::Attribute::Height, configs[id].height); - config.setAttribute(HWC2::Attribute::DpiX, configs[id].dpiX * 1000); - config.setAttribute(HWC2::Attribute::DpiY, configs[id].dpiY * 1000); - mConfigs.emplace(id, config); + for (const DisplayConfig& config : configs) { + mConfigs.emplace(config.getId(), config); } - mActiveConfigId = activeConfig; - mActiveColorMode = HAL_COLOR_MODE_NATIVE; - mColorModes.emplace((android_color_mode_t)HAL_COLOR_MODE_NATIVE); + mActiveConfigId = activeConfigId; mEdid = edid; + auto it = mConfigs.find(activeConfigId); + if (it == mConfigs.end()) { + ALOGE("%s: display:%" PRIu64 "missing config:%" PRIu32, __FUNCTION__, mId, + activeConfigId); + return HWC2::Error::NoResources; + } + + const auto& activeConfig = it->second; + const auto activeConfigString = activeConfig.toString(); + ALOGD("%s initializing display:%" PRIu64 " with config:%s", __FUNCTION__, mId, + activeConfigString.c_str()); + + mVsyncThread->start(activeConfig.getVsyncPeriod()); + return HWC2::Error::None; } @@ -116,14 +105,13 @@ HWC2::Error Display::updateParameters( std::unique_lock<std::recursive_mutex> lock(mStateMutex); - mVsyncPeriod = 1000 * 1000 * 1000 / refreshRateHz; - auto it = mConfigs.find(*mActiveConfigId); if (it == mConfigs.end()) { ALOGE("%s: failed to find config %" PRIu32, __func__, *mActiveConfigId); return HWC2::Error::NoResources; } - it->second.setAttribute(HWC2::Attribute::VsyncPeriod, mVsyncPeriod); + it->second.setAttribute(HWC2::Attribute::VsyncPeriod, + 1000 * 1000 * 1000 / refreshRateHz); it->second.setAttribute(HWC2::Attribute::Width, width); it->second.setAttribute(HWC2::Attribute::Height, height); it->second.setAttribute(HWC2::Attribute::DpiX, dpiX * 1000); @@ -256,7 +244,7 @@ HWC2::Error Display::getDisplayAttributeEnum(hwc2_config_t configId, return HWC2::Error::BadConfig; } - const Config& config = it->second; + const DisplayConfig& config = it->second; *outValue = config.getAttribute(attribute); DEBUG_LOG("%s: display:%" PRIu64 " attribute:%s value is %" PRIi32, __FUNCTION__, mId, attributeString.c_str(), *outValue); @@ -506,23 +494,11 @@ HWC2::Error Display::setActiveConfig(hwc2_config_t configId) { DEBUG_LOG("%s: display:%" PRIu64 " setting active config to %" PRIu32, __FUNCTION__, mId, configId); - std::unique_lock<std::recursive_mutex> lock(mStateMutex); - - if (mConfigs.find(configId) == mConfigs.end()) { - ALOGE("%s: display:%" PRIu64 " bad config:%" PRIu32, __FUNCTION__, mId, - configId); - return HWC2::Error::BadConfig; - } - - if (mActiveConfigId != configId) { - if (mComposer == nullptr) { - ALOGE("%s: display:%" PRIu64 " missing composer", __FUNCTION__, mId); - return HWC2::Error::NoResources; - } - mActiveConfigId = configId; - return mComposer->onActiveConfigChange(this); - } - return HWC2::Error::None; + hwc_vsync_period_change_constraints_t constraints; + constraints.desiredTimeNanos = 0; + constraints.seamlessRequired = false; + hwc_vsync_period_change_timeline_t timeline; + return setActiveConfigWithConstraints(configId, &constraints, &timeline); } HWC2::Error Display::setClientTarget(buffer_handle_t target, @@ -623,8 +599,8 @@ HWC2::Error Display::setPowerMode(int32_t intMode) { 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; + 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); @@ -646,10 +622,48 @@ HWC2::Error Display::setVsyncEnabled(int32_t intEnable) { } std::unique_lock<std::recursive_mutex> lock(mStateMutex); - DEBUG_LOG("%s: display:%" PRIu64 " setting vsync locked to %s", __FUNCTION__, - mId, enableString.c_str()); + return mVsyncThread->setVsyncEnabled(enable == HWC2::Vsync::Enable); +} + +HWC2::Error Display::setVsyncCallback(HWC2_PFN_VSYNC callback, + hwc2_callback_data_t data) { + DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, mId); + + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + return mVsyncThread->setVsyncCallback(callback, data); +} + +HWC2::Error Display::setVsync24Callback(HWC2_PFN_VSYNC_2_4 callback, + hwc2_callback_data_t data) { + DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, mId); + + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + return mVsyncThread->setVsync24Callback(callback, data); +} + +HWC2::Error Display::getDisplayVsyncPeriod( + hwc2_vsync_period_t* outVsyncPeriod) { + DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, mId); + + std::unique_lock<std::recursive_mutex> lock(mStateMutex); - mVsyncEnabled = enable; + if (!mActiveConfigId) { + ALOGE("%s : display:%" PRIu64 " no active config", __FUNCTION__, mId); + return HWC2::Error::BadConfig; + } + + const auto it = mConfigs.find(*mActiveConfigId); + if (it == mConfigs.end()) { + ALOGE("%s : display:%" PRIu64 " failed to find active config:%" PRIu32, + __FUNCTION__, mId, *mActiveConfigId); + return HWC2::Error::BadConfig; + } + const DisplayConfig& activeConfig = it->second; + + *outVsyncPeriod = static_cast<hwc2_vsync_period_t>( + activeConfig.getAttribute(HWC2::Attribute::VsyncPeriod)); return HWC2::Error::None; } @@ -735,7 +749,7 @@ HWC2::Error Display::getClientTargetSupport(uint32_t width, uint32_t height, return HWC2::Error::Unsupported; } - const Config& activeConfig = it->second; + const DisplayConfig& activeConfig = it->second; const uint32_t activeConfigWidth = static_cast<uint32_t>(activeConfig.getAttribute(HWC2::Attribute::Width)); const uint32_t activeConfigHeight = @@ -914,123 +928,95 @@ HWC2::Error Display::setDisplayBrightness(float brightness) { return HWC2::Error::Unsupported; } -void Display::Config::setAttribute(HWC2::Attribute attribute, int32_t value) { - mAttributes[attribute] = value; -} +HWC2::Error Display::setActiveConfigWithConstraints( + hwc2_config_t configId, + hwc_vsync_period_change_constraints_t* vsyncPeriodChangeConstraints, + hwc_vsync_period_change_timeline_t* outTimeline) { + DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, mId); -int32_t Display::Config::getAttribute(HWC2::Attribute attribute) const { - if (mAttributes.count(attribute) == 0) { - return -1; - } - return mAttributes.at(attribute); -} + std::unique_lock<std::recursive_mutex> lock(mStateMutex); -std::string Display::Config::toString() const { - std::string output; + if (vsyncPeriodChangeConstraints == nullptr || outTimeline == nullptr) { + return HWC2::Error::BadParameter; + } - auto widthIt = mAttributes.find(HWC2::Attribute::Width); - if (widthIt != mAttributes.end()) { - output += " w:" + std::to_string(widthIt->second); + if (mConfigs.find(configId) == mConfigs.end()) { + ALOGE("%s: display:%" PRIu64 " bad config:%" PRIu32, __FUNCTION__, mId, + configId); + return HWC2::Error::BadConfig; } - auto heightIt = mAttributes.find(HWC2::Attribute::Height); - if (heightIt != mAttributes.end()) { - output += " h:" + std::to_string(heightIt->second); + if (mActiveConfigId == configId) { + return HWC2::Error::None; } + mActiveConfigId = configId; - auto vsyncIt = mAttributes.find(HWC2::Attribute::VsyncPeriod); - if (vsyncIt != mAttributes.end()) { - output += " vsync:" + std::to_string(1e9 / vsyncIt->second); + if (mComposer == nullptr) { + ALOGE("%s: display:%" PRIu64 " missing composer", __FUNCTION__, mId); + return HWC2::Error::NoResources; } - auto dpiXIt = mAttributes.find(HWC2::Attribute::DpiX); - if (dpiXIt != mAttributes.end()) { - output += " dpi-x:" + std::to_string(dpiXIt->second / 1000.0f); + HWC2::Error error = mComposer->onActiveConfigChange(this); + if (error != HWC2::Error::None) { + ALOGE("%s: display:%" PRIu64 " composer failed to handle config change", + __FUNCTION__, mId); + return error; } - auto dpiYIt = mAttributes.find(HWC2::Attribute::DpiY); - if (dpiYIt != mAttributes.end()) { - output += " dpi-y:" + std::to_string(dpiYIt->second / 1000.0f); + hwc2_vsync_period_t vsyncPeriod; + error = getDisplayVsyncPeriod(&vsyncPeriod); + if (error != HWC2::Error::None) { + ALOGE("%s: display:%" PRIu64 " composer failed to handle config change", + __FUNCTION__, mId); + return error; } - return output; + return mVsyncThread->scheduleVsyncUpdate( + vsyncPeriod, vsyncPeriodChangeConstraints, outTimeline); } -// VsyncThread function -bool Display::VsyncThread::threadLoop() { - struct timespec rt; - if (clock_gettime(CLOCK_MONOTONIC, &rt) == -1) { - ALOGE("%s: error in vsync thread clock_gettime: %s", __FUNCTION__, - strerror(errno)); - return true; +HWC2::Error Display::getDisplayConnectionType(uint32_t* 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 = HWC2_DISPLAY_CONNECTION_TYPE_INTERNAL; + } else { + // Other devices default to the first display INTERNAL, others EXTERNAL. + *outType = mId == 0 ? HWC2_DISPLAY_CONNECTION_TYPE_INTERNAL + : HWC2_DISPLAY_CONNECTION_TYPE_EXTERNAL; } - const int logInterval = 60; - int64_t lastLogged = rt.tv_sec; - int sent = 0; - int lastSent = 0; - bool vsyncEnabled = false; - - struct timespec wait_time; - wait_time.tv_sec = 0; - wait_time.tv_nsec = mDisplay.mVsyncPeriod; - const int64_t kOneRefreshNs = mDisplay.mVsyncPeriod; - const int64_t kOneSecondNs = 1000ULL * 1000ULL * 1000ULL; - int64_t lastTimeNs = -1; - int64_t phasedWaitNs = 0; - int64_t currentNs = 0; - - while (true) { - clock_gettime(CLOCK_MONOTONIC, &rt); - currentNs = rt.tv_nsec + rt.tv_sec * kOneSecondNs; - - if (lastTimeNs < 0) { - phasedWaitNs = currentNs + kOneRefreshNs; - } else { - phasedWaitNs = - kOneRefreshNs * ((currentNs - lastTimeNs) / kOneRefreshNs + 1) + - lastTimeNs; - } - - wait_time.tv_sec = phasedWaitNs / kOneSecondNs; - wait_time.tv_nsec = phasedWaitNs - wait_time.tv_sec * kOneSecondNs; + return HWC2::Error::None; +} - int ret; - do { - ret = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &wait_time, NULL); - } while (ret == -1 && errno == EINTR); +HWC2::Error Display::setAutoLowLatencyMode(bool /*on*/) { + DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, mId); - lastTimeNs = phasedWaitNs; + return HWC2::Error::Unsupported; +} - std::unique_lock<std::recursive_mutex> lock(mDisplay.mStateMutex); - vsyncEnabled = (mDisplay.mVsyncEnabled == HWC2::Vsync::Enable); - lock.unlock(); +HWC2::Error Display::getSupportedContentTypes( + uint32_t* outNumSupportedContentTypes, + const uint32_t* /*outSupportedContentTypes*/) { + DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, mId); - if (!vsyncEnabled) { - continue; - } + if (outNumSupportedContentTypes != nullptr) { + *outNumSupportedContentTypes = 0; + } - lock.lock(); - const auto& callbackInfo = - mDisplay.mDevice.mCallbacks[HWC2::Callback::Vsync]; - auto vsync = reinterpret_cast<HWC2_PFN_VSYNC>(callbackInfo.pointer); - lock.unlock(); + return HWC2::Error::None; +} - if (vsync) { - DEBUG_LOG("%s: display:%" PRIu64 " calling vsync", __FUNCTION__, - mDisplay.mId); - vsync(callbackInfo.data, mDisplay.mId, lastTimeNs); - } +HWC2::Error Display::setContentType(int32_t contentTypeRaw) { + auto contentType = static_cast<HWC2::ContentType>(contentTypeRaw); + auto contentTypeString = to_string(contentType); + DEBUG_LOG("%s: display:%" PRIu64 " content type:%s", __FUNCTION__, mId, + contentTypeString.c_str()); - int64_t lastSentInterval = rt.tv_sec - lastLogged; - if (lastSentInterval >= logInterval) { - DEBUG_LOG("sent %d syncs in %" PRId64 "s", sent - lastSent, - lastSentInterval); - lastLogged = rt.tv_sec; - lastSent = sent; - } - ++sent; + if (contentType != HWC2::ContentType::None) { + return HWC2::Error::Unsupported; } - return false; + + return HWC2::Error::None; } } // namespace android diff --git a/system/hwc2/Display.h b/system/hwc2/Display.h index 47291c7e..65c20a3b 100644 --- a/system/hwc2/Display.h +++ b/system/hwc2/Display.h @@ -18,9 +18,9 @@ #define ANDROID_HWC_DISPLAY_H #include <android/hardware/graphics/common/1.0/types.h> -#include <utils/Thread.h> #include <array> +#include <mutex> #include <optional> #include <set> #include <thread> @@ -29,9 +29,11 @@ #include "Common.h" #include "Composer.h" +#include "DisplayConfig.h" #include "DisplayFinder.h" #include "FencedBuffer.h" #include "Layer.h" +#include "VsyncThread.h" namespace android { @@ -45,7 +47,7 @@ struct ColorTransformWithMatrix { class Display { public: - Display(Device& device, Composer* composer, hwc2_display_t id); + Display(Composer* composer, hwc2_display_t id); ~Display(); Display(const Display& display) = delete; @@ -54,7 +56,8 @@ class Display { Display(Display&& display) = delete; Display& operator=(Display&& display) = delete; - HWC2::Error init(const std::vector<DisplayConfig>& configs, int activeConfig, + HWC2::Error init( + const std::vector<DisplayConfig>& configs, hwc2_config_t activeConfigId, const std::optional<std::vector<uint8_t>>& edid = std::nullopt); HWC2::Error updateParameters( @@ -116,6 +119,17 @@ class Display { HWC2::Error setOutputBuffer(buffer_handle_t buffer, int32_t releaseFence); HWC2::Error setPowerMode(int32_t mode); HWC2::Error setVsyncEnabled(int32_t enabled); + HWC2::Error setVsyncCallback(HWC2_PFN_VSYNC callback, + hwc2_callback_data_t data); + HWC2::Error setVsync24Callback(HWC2_PFN_VSYNC_2_4 callback, + hwc2_callback_data_t data); + HWC2::Error getDisplayVsyncPeriod(hwc2_vsync_period_t* outVsyncPeriod); + HWC2::Error setActiveConfigWithConstraints( + hwc2_config_t config, + hwc_vsync_period_change_constraints_t* vsyncPeriodChangeConstraints, + hwc_vsync_period_change_timeline_t* timeline); + HWC2::Error getDisplayConnectionType(uint32_t* outType); + HWC2::Error validate(uint32_t* outNumTypes, uint32_t* outNumRequests); HWC2::Error updateLayerZ(hwc2_layer_t layerId, uint32_t z); HWC2::Error getClientTargetSupport(uint32_t width, uint32_t height, @@ -128,33 +142,15 @@ class Display { uint32_t* outCapabilities); HWC2::Error getDisplayBrightnessSupport(bool* out_support); HWC2::Error setDisplayBrightness(float brightness); + HWC2::Error setAutoLowLatencyMode(bool on); + HWC2::Error getSupportedContentTypes( + uint32_t* outNumSupportedContentTypes, + const uint32_t* outSupportedContentTypes); + HWC2::Error setContentType(int32_t contentType); void lock() { mStateMutex.lock(); } void unlock() { mStateMutex.unlock(); } private: - class Config { - public: - Config(hwc2_config_t configId) : mId(configId) {} - - Config(const Config& display) = default; - Config& operator=(const Config& display) = default; - - Config(Config&& display) = default; - Config& operator=(Config&& display) = default; - - hwc2_config_t getId() const { return mId; } - void setId(hwc2_config_t id) { mId = id; } - - int32_t getAttribute(HWC2::Attribute attribute) const; - void setAttribute(HWC2::Attribute attribute, int32_t value); - - std::string toString() const; - - private: - hwc2_config_t mId; - std::unordered_map<HWC2::Attribute, int32_t> mAttributes; - }; - // Stores changes requested from the device upon calling prepare(). // Handles change request to: // - Layer composition type. @@ -194,23 +190,6 @@ class Display { std::unordered_map<hwc2_layer_t, HWC2::LayerRequest> mLayerRequests; }; - // Generate sw vsync signal - class VsyncThread : public Thread { - public: - VsyncThread(Display& display) : mDisplay(display) {} - virtual ~VsyncThread() {} - - VsyncThread(const VsyncThread&) = default; - VsyncThread& operator=(const VsyncThread&) = default; - - VsyncThread(VsyncThread&&) = default; - VsyncThread& operator=(VsyncThread&&) = default; - - private: - Display& mDisplay; - bool threadLoop() final; - }; - private: // The state of this display should only be modified from // SurfaceFlinger's main loop, with the exception of when dump is @@ -218,14 +197,11 @@ class Display { // call, all public calls into Display must acquire this mutex. mutable std::recursive_mutex mStateMutex; - Device& mDevice; Composer* mComposer = nullptr; const hwc2_display_t mId; std::string mName; HWC2::DisplayType mType = HWC2::DisplayType::Physical; HWC2::PowerMode mPowerMode = HWC2::PowerMode::Off; - HWC2::Vsync mVsyncEnabled = HWC2::Vsync::Invalid; - uint32_t mVsyncPeriod; sp<VsyncThread> mVsyncThread; FencedBuffer mClientTarget; // Will only be non-null after the Display has been validated and @@ -239,9 +215,9 @@ class Display { std::vector<hwc2_display_t> mReleaseLayerIds; std::vector<int32_t> mReleaseFences; std::optional<hwc2_config_t> mActiveConfigId; - std::unordered_map<hwc2_config_t, Config> mConfigs; - std::set<android_color_mode_t> mColorModes; - android_color_mode_t mActiveColorMode; + std::unordered_map<hwc2_config_t, DisplayConfig> mConfigs; + std::set<android_color_mode_t> mColorModes = {HAL_COLOR_MODE_NATIVE}; + android_color_mode_t mActiveColorMode = HAL_COLOR_MODE_NATIVE; std::optional<ColorTransformWithMatrix> mColorTransform; std::optional<std::vector<uint8_t>> mEdid; }; diff --git a/system/hwc2/DisplayConfig.cpp b/system/hwc2/DisplayConfig.cpp new file mode 100644 index 00000000..b05f9076 --- /dev/null +++ b/system/hwc2/DisplayConfig.cpp @@ -0,0 +1,124 @@ +/* + * Copyright 2021 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 "DisplayConfig.h" + +#include <unordered_map> + +namespace { + +template <class T> +inline void hashCombine(size_t& hash, const T& value) { + std::hash<T> hasher; + hash ^= hasher(value) + 0x9e3779b9 + (hash << 6) + (hash >> 2); +} + +} // namespace + +void DisplayConfig::setAttribute(HWC2::Attribute attribute, int32_t value) { + if (attribute == HWC2::Attribute::Width) { + mWidth = value; + } + if (attribute == HWC2::Attribute::Height) { + mHeight = value; + } + if (attribute == HWC2::Attribute::DpiX) { + mDpiX = value; + } + if (attribute == HWC2::Attribute::DpiY) { + mDpiY = value; + } + if (attribute == HWC2::Attribute::VsyncPeriod) { + mVsyncPeriodNanos = value; + } + if (attribute == HWC2::Attribute::ConfigGroup) { + mConfigGroup = value; + } +} + +int32_t DisplayConfig::getAttribute(HWC2::Attribute attribute) const { + if (attribute == HWC2::Attribute::Width) { + return mWidth; + } + if (attribute == HWC2::Attribute::Height) { + return mHeight; + } + if (attribute == HWC2::Attribute::DpiX) { + return mDpiX; + } + if (attribute == HWC2::Attribute::DpiY) { + return mDpiY; + } + if (attribute == HWC2::Attribute::VsyncPeriod) { + return mVsyncPeriodNanos; + } + if (attribute == HWC2::Attribute::ConfigGroup) { + return mConfigGroup; + } + return -1; +} + +std::string DisplayConfig::toString() const { + std::string output; + output += " w:" + std::to_string(mWidth); + output += " h:" + std::to_string(mHeight); + output += " dpi-x:" + std::to_string(mDpiX / 1000.0f); + output += " dpi-y:" + std::to_string(mDpiY / 1000.0f); + output += " vsync:" + std::to_string(1e9 / mVsyncPeriodNanos); + output += " config-group:" + std::to_string(mConfigGroup); + return output; +} + +/*static*/ +void DisplayConfig::addConfigGroups(std::vector<DisplayConfig>* configs) { + // From /hardware/interfaces/graphics/composer/2.4/IComposerClient.hal: + // "Configurations which share the same config group are similar in all + // attributes except for the vsync period." + struct ConfigForGroupHash { + size_t operator()(const DisplayConfig& config) const { + size_t hash = 0; + hashCombine(hash, config.mWidth); + hashCombine(hash, config.mHeight); + hashCombine(hash, config.mDpiX); + hashCombine(hash, config.mDpiY); + return hash; + } + }; + struct ConfigForGroupEq { + size_t operator()(const DisplayConfig& a, const DisplayConfig& b) const { + if (a.mWidth != b.mWidth) { + return a.mWidth < b.mWidth; + } + if (a.mHeight != b.mHeight) { + return a.mHeight < b.mHeight; + } + if (a.mDpiX != b.mDpiX) { + return a.mDpiX < b.mDpiX; + } + return a.mDpiY < b.mDpiY; + } + }; + + std::unordered_map<DisplayConfig, int32_t, ConfigForGroupHash, + ConfigForGroupEq> + configToConfigGroup; + + for (auto& config : *configs) { + auto [it, inserted] = + configToConfigGroup.try_emplace(config, configToConfigGroup.size()); + config.setConfigGroup(it->second); + } +}
\ No newline at end of file diff --git a/system/hwc2/DisplayConfig.h b/system/hwc2/DisplayConfig.h new file mode 100644 index 00000000..f7502d70 --- /dev/null +++ b/system/hwc2/DisplayConfig.h @@ -0,0 +1,84 @@ +/* + * Copyright 2021 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. + */ + +#ifndef ANDROID_HWC_DISPLAYCONFIG_H +#define ANDROID_HWC_DISPLAYCONFIG_H + +#include <android/hardware/graphics/common/1.0/types.h> + +#include <vector> + +#include "Common.h" + +class DisplayConfig { + public: + DisplayConfig(hwc2_config_t configId) : mId(configId) {} + + DisplayConfig(hwc2_config_t configId, int32_t width, int32_t height, + int32_t dpiX, int32_t dpiY, int32_t vsyncPeriodNanos) + : mId(configId), + mWidth(width), + mHeight(height), + mDpiX(dpiX), + mDpiY(dpiY), + mVsyncPeriodNanos(vsyncPeriodNanos) {} + + DisplayConfig(const DisplayConfig& other) = default; + DisplayConfig& operator=(DisplayConfig& other) = default; + + DisplayConfig(DisplayConfig&& other) = default; + DisplayConfig& operator=(DisplayConfig&& other) = default; + + hwc2_config_t getId() const { return mId; } + void setId(hwc2_config_t id) { mId = id; } + + int32_t getAttribute(HWC2::Attribute attribute) const; + void setAttribute(HWC2::Attribute attribute, int32_t value); + + int32_t getWidth() const { return mWidth; } + void setWidth(int32_t width) { mWidth = width; } + + int32_t getHeight() const { return mHeight; } + void getHeight(int32_t height) { mHeight = height; } + + int32_t getDpiX() const { return mDpiX; } + void setDpiX(int32_t dpi) { mDpiX = dpi; } + + int32_t getDpiY() const { return mDpiY; } + void setDpiY(int32_t dpi) { mDpiY = dpi; } + + int32_t getVsyncPeriod() const { return mVsyncPeriodNanos; } + void setVsyncPeriod(int32_t vsync) { mVsyncPeriodNanos = vsync; } + + int32_t getConfigGroup() const { return mConfigGroup; } + void setConfigGroup(int32_t group) { mConfigGroup = group; } + + std::string toString() const; + + static void addConfigGroups(std::vector<DisplayConfig>* configs); + + private: + hwc2_config_t mId; + + int32_t mWidth; + int32_t mHeight; + int32_t mDpiX; + int32_t mDpiY; + int32_t mVsyncPeriodNanos; + int32_t mConfigGroup; +}; + +#endif
\ No newline at end of file diff --git a/system/hwc2/DisplayFinder.cpp b/system/hwc2/DisplayFinder.cpp index 87a93f35..2a5655c6 100644 --- a/system/hwc2/DisplayFinder.cpp +++ b/system/hwc2/DisplayFinder.cpp @@ -27,25 +27,36 @@ namespace android { namespace { +constexpr int32_t HertzToPeriodNanos(uint32_t hertz) { + return 1000 * 1000 * 1000 / hertz; +} + HWC2::Error findCuttlefishDisplays(std::vector<DisplayMultiConfigs>& displays) { DEBUG_LOG("%s", __FUNCTION__); // TODO: replace with initializing directly from DRM info. const auto deviceConfig = cuttlefish::GetDeviceConfig(); - int displayId = 0; + hwc2_display_t displayId = 0; + hwc2_config_t configId = 0; for (const auto& deviceDisplayConfig : deviceConfig.display_config()) { DisplayMultiConfigs display = { - .id = displayId, - .activeConfigId = 0, - .configs = {{displayId, - deviceDisplayConfig.width(), - deviceDisplayConfig.height(), - deviceDisplayConfig.dpi(), - deviceDisplayConfig.dpi(), - deviceDisplayConfig.refresh_rate_hz()}}, + .displayId = displayId, + .activeConfigId = configId, + .configs = + { + DisplayConfig(configId, // + deviceDisplayConfig.width(), // + deviceDisplayConfig.height(), // + deviceDisplayConfig.dpi(), // + deviceDisplayConfig.dpi(), // + HertzToPeriodNanos( + deviceDisplayConfig.refresh_rate_hz()) // + ), // + }, }; displays.push_back(display); + ++configId; ++displayId; } @@ -68,45 +79,43 @@ static int getVsyncHzFromProperty() { return static_cast<int>(vsyncPeriod); } -HWC2::Error findGoldfishPrimaryDisplay(std::vector<DisplayMultiConfigs>& displays) { +HWC2::Error findGoldfishPrimaryDisplay( + std::vector<DisplayMultiConfigs>& displays) { DEBUG_LOG("%s", __FUNCTION__); DEFINE_AND_VALIDATE_HOST_CONNECTION hostCon->lock(); - int activeConfigId; - const int refreshRateHz = getVsyncHzFromProperty(); + const int32_t vsyncPeriodNanos = HertzToPeriodNanos(getVsyncHzFromProperty()); DisplayMultiConfigs display; - display.id = 0; + display.displayId = 0; if (rcEnc->hasHWCMultiConfigs()) { - int count= rcEnc->rcGetFBDisplayConfigsCount(rcEnc); + int count = rcEnc->rcGetFBDisplayConfigsCount(rcEnc); if (count <= 0) { - ALOGE("%s failed to allocate primary display, config count %d", __func__, count); + ALOGE("%s failed to allocate primary display, config count %d", __func__, + count); return HWC2::Error::NoResources; } display.activeConfigId = rcEnc->rcGetFBDisplayActiveConfig(rcEnc); - for(int configId = 0; configId < count; configId++) { - display.configs.push_back({ - 0, - rcEnc->rcGetFBDisplayConfigsParam( - rcEnc, configId, FB_WIDTH), - rcEnc->rcGetFBDisplayConfigsParam( - rcEnc, configId, FB_HEIGHT), - rcEnc->rcGetFBDisplayConfigsParam( - rcEnc, configId, FB_XDPI), - rcEnc->rcGetFBDisplayConfigsParam( - rcEnc, configId, FB_YDPI), - refreshRateHz, - }); + for (int configId = 0; configId < count; configId++) { + display.configs.push_back(DisplayConfig( + configId, // + rcEnc->rcGetFBDisplayConfigsParam(rcEnc, configId, FB_WIDTH), // + rcEnc->rcGetFBDisplayConfigsParam(rcEnc, configId, FB_HEIGHT), // + rcEnc->rcGetFBDisplayConfigsParam(rcEnc, configId, FB_XDPI), // + rcEnc->rcGetFBDisplayConfigsParam(rcEnc, configId, FB_YDPI), // + vsyncPeriodNanos // + )); } } else { display.activeConfigId = 0; - display.configs.push_back({ - 0, - rcEnc->rcGetFBParam(rcEnc, FB_WIDTH), - rcEnc->rcGetFBParam(rcEnc, FB_HEIGHT), - rcEnc->rcGetFBParam(rcEnc, FB_XDPI), - rcEnc->rcGetFBParam(rcEnc, FB_YDPI), - refreshRateHz}); + display.configs.push_back(DisplayConfig( + 0, // + rcEnc->rcGetFBParam(rcEnc, FB_WIDTH), // + rcEnc->rcGetFBParam(rcEnc, FB_HEIGHT), // + rcEnc->rcGetFBParam(rcEnc, FB_XDPI), // + rcEnc->rcGetFBParam(rcEnc, FB_YDPI), // + vsyncPeriodNanos // + )); } hostCon->unlock(); @@ -115,7 +124,8 @@ HWC2::Error findGoldfishPrimaryDisplay(std::vector<DisplayMultiConfigs>& display return HWC2::Error::None; } -HWC2::Error findGoldfishSecondaryDisplays(std::vector<DisplayMultiConfigs>& displays) { +HWC2::Error findGoldfishSecondaryDisplays( + std::vector<DisplayMultiConfigs>& displays) { DEBUG_LOG("%s", __FUNCTION__); static constexpr const char kExternalDisplayProp[] = @@ -147,21 +157,23 @@ HWC2::Error findGoldfishSecondaryDisplays(std::vector<DisplayMultiConfigs>& disp propIntParts.push_back(propIntPart); } - int secondaryDisplayId = 1; + hwc2_display_t secondaryDisplayId = 1; + hwc2_config_t secondaryConfigId = 1; while (!propIntParts.empty()) { DisplayMultiConfigs display; - display.id = secondaryDisplayId; + display.displayId = secondaryDisplayId; display.activeConfigId = 0; - display.configs.push_back(DisplayConfig{ - .id = secondaryDisplayId, - .width = propIntParts[1], - .height = propIntParts[2], - .dpiX = propIntParts[3], - .dpiY = propIntParts[3], - .refreshRateHz = 160, - }); + display.configs.push_back(DisplayConfig( + secondaryConfigId, // + /*width=*/propIntParts[1], // + /*heighth=*/propIntParts[2], // + /*dpiXh=*/propIntParts[3], // + /*dpiYh=*/propIntParts[3], // + /*vsyncPeriod=*/HertzToPeriodNanos(160) // + )); displays.push_back(display); + ++secondaryConfigId; ++secondaryDisplayId; propIntParts.erase(propIntParts.begin(), propIntParts.begin() + 5); @@ -188,11 +200,23 @@ HWC2::Error findGoldfishDisplays(std::vector<DisplayMultiConfigs>& displays) { } // namespace HWC2::Error findDisplays(std::vector<DisplayMultiConfigs>& displays) { + HWC2::Error error = HWC2::Error::None; if (IsCuttlefish()) { - return findCuttlefishDisplays(displays); + error = findCuttlefishDisplays(displays); } else { - return findGoldfishDisplays(displays); + error = findGoldfishDisplays(displays); + } + + if (error != HWC2::Error::None) { + ALOGE("%s failed to find displays", __FUNCTION__); + return error; + } + + for (auto& display : displays) { + DisplayConfig::addConfigGroups(&display.configs); } + + return HWC2::Error::None; } } // namespace android diff --git a/system/hwc2/DisplayFinder.h b/system/hwc2/DisplayFinder.h index 44a09a60..b9e1b19e 100644 --- a/system/hwc2/DisplayFinder.h +++ b/system/hwc2/DisplayFinder.h @@ -20,21 +20,13 @@ #include <vector> #include "Common.h" +#include "DisplayConfig.h" namespace android { -struct DisplayConfig { - int id; - int width; - int height; - int dpiX; - int dpiY; - int refreshRateHz; -}; - struct DisplayMultiConfigs { - int id; - int activeConfigId; + hwc2_display_t displayId; + hwc2_config_t activeConfigId; // Modes that this display can be configured to use. std::vector<DisplayConfig> configs; }; diff --git a/system/hwc2/DrmPresenter.h b/system/hwc2/DrmPresenter.h index d65ce9d1..da511954 100644 --- a/system/hwc2/DrmPresenter.h +++ b/system/hwc2/DrmPresenter.h @@ -26,9 +26,9 @@ #include <memory> #include <vector> -#include "drmhwcgralloc.h" #include "Common.h" #include "android/base/synchronization/AndroidLock.h" +#include "drmhwcgralloc.h" namespace android { diff --git a/system/hwc2/HostComposer.cpp b/system/hwc2/HostComposer.cpp index d0e1ba6e..ede56976 100644 --- a/system/hwc2/HostComposer.cpp +++ b/system/hwc2/HostComposer.cpp @@ -203,7 +203,7 @@ HWC2::Error HostComposer::createHostComposerDisplayInfo( displayInfo.hostDisplayId = hostDisplayId; if (displayInfo.compositionResultBuffer) { - FreeDisplayColorBuffer(displayInfo.compositionResultBuffer); + FreeDisplayColorBuffer(displayInfo.compositionResultBuffer); } displayInfo.compositionResultBuffer = AllocateDisplayColorBuffer(displayWidth, displayHeight); @@ -572,7 +572,7 @@ HWC2::Error HostComposer::presentDisplay(Display* display, p2->numLayers = numLayer; } - void *buffer; + void* buffer; uint32_t bufferSize; if (hostCompositionV1) { buffer = (void*)p; @@ -676,8 +676,8 @@ HWC2::Error HostComposer::onActiveConfigChange(Display* display) { DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, display->getId()); HWC2::Error error = createHostComposerDisplayInfo(display, display->getId()); if (error != HWC2::Error::None) { - ALOGE("%s failed to update host info for display:%" PRIu64, - __FUNCTION__, display->getId()); + ALOGE("%s failed to update host info for display:%" PRIu64, __FUNCTION__, + display->getId()); return error; } return HWC2::Error::None; diff --git a/system/hwc2/VsyncThread.cpp b/system/hwc2/VsyncThread.cpp new file mode 100644 index 00000000..dbefcc65 --- /dev/null +++ b/system/hwc2/VsyncThread.cpp @@ -0,0 +1,193 @@ +/* + * Copyright 2021 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 "VsyncThread.h" + +#include <thread> + +namespace android { +namespace { + +std::chrono::time_point<std::chrono::steady_clock> asTimePoint(int64_t nanos) { + return std::chrono::time_point<std::chrono::steady_clock>( + std::chrono::nanoseconds(nanos)); +} + +hwc2_vsync_period_t asNanos(std::chrono::nanoseconds duration) { + return duration.count(); +} + +int64_t asNanos(std::chrono::time_point<std::chrono::steady_clock> time) { + std::chrono::time_point<std::chrono::steady_clock> zero( + std::chrono::nanoseconds(0)); + return std::chrono::duration_cast<std::chrono::nanoseconds>(time - zero) + .count(); +} + +// Returns the timepoint of the next vsync after the 'now' timepoint that is +// a multiple of 'vsyncPeriod' in-phase/offset-from 'previousSync'. +// +// Some examples: +// * vsyncPeriod=50ns previousVsync=500ns now=510ns => 550ns +// * vsyncPeriod=50ns previousVsync=300ns now=510ns => 550ns +// * vsyncPeriod=50ns previousVsync=500ns now=550ns => 550ns +std::chrono::time_point<std::chrono::steady_clock> GetNextVsyncInPhase( + std::chrono::nanoseconds vsyncPeriod, + std::chrono::time_point<std::chrono::steady_clock> previousVsync, + std::chrono::time_point<std::chrono::steady_clock> now) { + const auto elapsed = std::chrono::nanoseconds(now - previousVsync); + const auto nextMultiple = (elapsed / vsyncPeriod) + 1; + return previousVsync + (nextMultiple * vsyncPeriod); +} + +} // namespace + +VsyncThread::VsyncThread(hwc2_display_t id) : mDisplayId(id) { + mPreviousVsync = std::chrono::steady_clock::now() - mVsyncPeriod; +} + +HWC2::Error VsyncThread::start(hwc2_vsync_period_t vsyncPeriod) { + DEBUG_LOG("%s for display:%" PRIu64, __FUNCTION__, mDisplayId); + + mVsyncPeriod = std::chrono::nanoseconds(vsyncPeriod); + + const std::string threadName = + "display_" + std::to_string(mDisplayId) + "_vsync_thread"; + this->run(threadName.c_str(), ANDROID_PRIORITY_URGENT_DISPLAY); + + return HWC2::Error::None; +} + +HWC2::Error VsyncThread::setVsyncCallback(HWC2_PFN_VSYNC callback, + hwc2_callback_data_t callbackData) { + DEBUG_LOG("%s for display:%" PRIu64, __FUNCTION__, mDisplayId); + + std::unique_lock<std::mutex> lock(mStateMutex); + + mVsyncCallback = callback; + mVsyncCallbackData = callbackData; + + return HWC2::Error::None; +} + +HWC2::Error VsyncThread::setVsync24Callback(HWC2_PFN_VSYNC_2_4 callback, + hwc2_callback_data_t callbackData) { + DEBUG_LOG("%s for display:%" PRIu64, __FUNCTION__, mDisplayId); + + std::unique_lock<std::mutex> lock(mStateMutex); + + mVsync24Callback = callback; + mVsync24CallbackData = callbackData; + + return HWC2::Error::None; +} + +HWC2::Error VsyncThread::setVsyncEnabled(bool enabled) { + DEBUG_LOG("%s for display:%" PRIu64 " enabled:%d", __FUNCTION__, mDisplayId, + enabled); + + std::unique_lock<std::mutex> lock(mStateMutex); + + mVsyncEnabled = enabled; + + return HWC2::Error::None; +} + +HWC2::Error VsyncThread::scheduleVsyncUpdate( + hwc2_vsync_period_t newVsyncPeriod, + hwc_vsync_period_change_constraints_t* newVsyncPeriodConstraints, + hwc_vsync_period_change_timeline_t* outTimeline) { + DEBUG_LOG("%s for display:%" PRIu64, __FUNCTION__, mDisplayId); + + PendingUpdate update; + update.period = std::chrono::nanoseconds(newVsyncPeriod); + update.updateAfter = asTimePoint(newVsyncPeriodConstraints->desiredTimeNanos); + + std::unique_lock<std::mutex> lock(mStateMutex); + mPendingUpdate.emplace(std::move(update)); + + auto nextVsync = + GetNextVsyncInPhase(mVsyncPeriod, mPreviousVsync, update.updateAfter); + + outTimeline->newVsyncAppliedTimeNanos = asNanos(nextVsync); + outTimeline->refreshRequired = false; + outTimeline->refreshTimeNanos = 0; + + return HWC2::Error::None; +} + +std::chrono::nanoseconds VsyncThread::updateVsyncPeriodLocked( + std::chrono::time_point<std::chrono::steady_clock> now) { + if (mPendingUpdate && now > mPendingUpdate->updateAfter) { + mVsyncPeriod = mPendingUpdate->period; + mPendingUpdate.reset(); + } + + return mVsyncPeriod; +} + +bool VsyncThread::threadLoop() { + DEBUG_LOG("%s: for display:%" PRIu64 " started", __FUNCTION__, mDisplayId); + + std::chrono::nanoseconds vsyncPeriod = mVsyncPeriod; + + int vsyncs = 0; + auto previousLog = std::chrono::steady_clock::now(); + while (true) { + auto now = std::chrono::steady_clock::now(); + + auto nextVsync = GetNextVsyncInPhase(vsyncPeriod, mPreviousVsync, now); + std::this_thread::sleep_until(nextVsync); + + { + std::unique_lock<std::mutex> lock(mStateMutex); + + mPreviousVsync = nextVsync; + + // Display has finished refreshing at previous vsync period. Update the + // vsync period if there was a pending update. + vsyncPeriod = updateVsyncPeriodLocked(mPreviousVsync); + } + + if (mVsyncEnabled) { + if (mVsync24Callback) { + DEBUG_LOG("%s: for display:%" PRIu64 " calling vsync_2_4", __FUNCTION__, + mDisplayId); + mVsync24Callback(mVsync24CallbackData, mDisplayId, asNanos(nextVsync), + asNanos(vsyncPeriod)); + } else if (mVsyncCallback) { + DEBUG_LOG("%s: for display:%" PRIu64 " calling vsync", __FUNCTION__, + mDisplayId); + mVsyncCallback(mVsyncCallbackData, mDisplayId, asNanos(nextVsync)); + } + } + + static constexpr const int kLogIntervalSeconds = 60; + if (now > (previousLog + std::chrono::seconds(kLogIntervalSeconds))) { + DEBUG_LOG("%s: for display:%" PRIu64 " send %" PRIu32 + " in last %d seconds", + __FUNCTION__, mDisplayId, vsyncs, kLogIntervalSeconds); + previousLog = now; + vsyncs = 0; + } + ++vsyncs; + } + + DEBUG_LOG("%s: for display:%" PRIu64 " started", __FUNCTION__, mDisplayId); + return false; +} + +} // namespace android diff --git a/system/hwc2/VsyncThread.h b/system/hwc2/VsyncThread.h new file mode 100644 index 00000000..e7c237c9 --- /dev/null +++ b/system/hwc2/VsyncThread.h @@ -0,0 +1,86 @@ +/* + * Copyright 2021 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. + */ + +#ifndef ANDROID_HWC_VSYNCTHREAD_H +#define ANDROID_HWC_VSYNCTHREAD_H + +#include <android/hardware/graphics/common/1.0/types.h> +#include <utils/Thread.h> + +#include <chrono> +#include <mutex> +#include <optional> + +#include "Common.h" + +namespace android { + +// Generates Vsync signals in software. +class VsyncThread : public Thread { + public: + VsyncThread(hwc2_display_t id); + virtual ~VsyncThread() {} + + VsyncThread(const VsyncThread&) = default; + VsyncThread& operator=(const VsyncThread&) = default; + + VsyncThread(VsyncThread&&) = default; + VsyncThread& operator=(VsyncThread&&) = default; + + HWC2::Error start(hwc2_vsync_period_t period); + + HWC2::Error setVsyncCallback(HWC2_PFN_VSYNC callback, + hwc2_callback_data_t callbackData); + HWC2::Error setVsync24Callback(HWC2_PFN_VSYNC_2_4 callback, + hwc2_callback_data_t callbackData); + + HWC2::Error setVsyncEnabled(bool enabled); + + HWC2::Error scheduleVsyncUpdate( + hwc2_vsync_period_t newVsyncPeriod, + hwc_vsync_period_change_constraints_t* newVsyncPeriodChangeConstraints, + hwc_vsync_period_change_timeline_t* timeline); + + private: + bool threadLoop() final; + + std::chrono::nanoseconds updateVsyncPeriodLocked( + std::chrono::time_point<std::chrono::steady_clock> now); + + const hwc2_display_t mDisplayId; + + std::mutex mStateMutex; + + HWC2_PFN_VSYNC mVsyncCallback; + hwc2_callback_data_t mVsyncCallbackData = nullptr; + + HWC2_PFN_VSYNC_2_4 mVsync24Callback; + hwc2_callback_data_t mVsync24CallbackData = nullptr; + + bool mVsyncEnabled = false; + std::chrono::nanoseconds mVsyncPeriod; + std::chrono::time_point<std::chrono::steady_clock> mPreviousVsync; + + struct PendingUpdate { + std::chrono::nanoseconds period; + std::chrono::time_point<std::chrono::steady_clock> updateAfter; + }; + std::optional<PendingUpdate> mPendingUpdate; +}; + +} // namespace android + +#endif
\ No newline at end of file |