| /* |
| * Copyright (C) 2007 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| // TODO(b/129481165): remove the #pragma below and fix conversion issues |
| #pragma clang diagnostic push |
| #pragma clang diagnostic ignored "-Wconversion" |
| #pragma clang diagnostic ignored "-Wextra" |
| |
| //#define LOG_NDEBUG 0 |
| #define ATRACE_TAG ATRACE_TAG_GRAPHICS |
| |
| #include "SurfaceFlinger.h" |
| |
| #include <aidl/android/hardware/power/Boost.h> |
| #include <android-base/parseint.h> |
| #include <android-base/properties.h> |
| #include <android-base/stringprintf.h> |
| #include <android-base/strings.h> |
| #include <android/configuration.h> |
| #include <android/gui/IDisplayEventConnection.h> |
| #include <android/gui/StaticDisplayInfo.h> |
| #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> |
| #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h> |
| #include <android/hardware/configstore/1.1/types.h> |
| #include <android/native_window.h> |
| #include <android/os/IInputFlinger.h> |
| #include <binder/IPCThreadState.h> |
| #include <binder/IServiceManager.h> |
| #include <binder/PermissionCache.h> |
| #include <com_android_graphics_surfaceflinger_flags.h> |
| #include <common/FlagManager.h> |
| #include <compositionengine/CompositionEngine.h> |
| #include <compositionengine/CompositionRefreshArgs.h> |
| #include <compositionengine/Display.h> |
| #include <compositionengine/DisplayColorProfile.h> |
| #include <compositionengine/DisplayColorProfileCreationArgs.h> |
| #include <compositionengine/DisplayCreationArgs.h> |
| #include <compositionengine/LayerFECompositionState.h> |
| #include <compositionengine/OutputLayer.h> |
| #include <compositionengine/RenderSurface.h> |
| #include <compositionengine/impl/DisplayColorProfile.h> |
| #include <compositionengine/impl/OutputCompositionState.h> |
| #include <compositionengine/impl/OutputLayerCompositionState.h> |
| #include <configstore/Utils.h> |
| #include <cutils/compiler.h> |
| #include <cutils/properties.h> |
| #include <fmt/format.h> |
| #include <ftl/algorithm.h> |
| #include <ftl/concat.h> |
| #include <ftl/fake_guard.h> |
| #include <ftl/future.h> |
| #include <ftl/unit.h> |
| #include <gui/AidlStatusUtil.h> |
| #include <gui/BufferQueue.h> |
| #include <gui/DebugEGLImageTracker.h> |
| #include <gui/IProducerListener.h> |
| #include <gui/LayerMetadata.h> |
| #include <gui/LayerState.h> |
| #include <gui/Surface.h> |
| #include <gui/SurfaceComposerClient.h> |
| #include <gui/TraceUtils.h> |
| #include <hidl/ServiceManagement.h> |
| #include <layerproto/LayerProtoParser.h> |
| #include <linux/sched/types.h> |
| #include <log/log.h> |
| #include <private/android_filesystem_config.h> |
| #include <private/gui/SyncFeatures.h> |
| #include <processgroup/processgroup.h> |
| #include <renderengine/RenderEngine.h> |
| #include <renderengine/impl/ExternalTexture.h> |
| #include <scheduler/FrameTargeter.h> |
| #include <sys/types.h> |
| #include <ui/ColorSpace.h> |
| #include <ui/DebugUtils.h> |
| #include <ui/DisplayId.h> |
| #include <ui/DisplayMode.h> |
| #include <ui/DisplayStatInfo.h> |
| #include <ui/DisplayState.h> |
| #include <ui/DynamicDisplayInfo.h> |
| #include <ui/GraphicBufferAllocator.h> |
| #include <ui/HdrRenderTypeUtils.h> |
| #include <ui/LayerStack.h> |
| #include <ui/PixelFormat.h> |
| #include <ui/StaticDisplayInfo.h> |
| #include <unistd.h> |
| #include <utils/StopWatch.h> |
| #include <utils/String16.h> |
| #include <utils/String8.h> |
| #include <utils/Timers.h> |
| #include <utils/misc.h> |
| #include <algorithm> |
| #include <cerrno> |
| #include <cinttypes> |
| #include <cmath> |
| #include <cstdint> |
| #include <filesystem> |
| #include <functional> |
| #include <memory> |
| #include <mutex> |
| #include <optional> |
| #include <string> |
| #include <type_traits> |
| #include <unordered_map> |
| #include <vector> |
| |
| #include <common/FlagManager.h> |
| #include <gui/LayerStatePermissions.h> |
| #include <gui/SchedulingPolicy.h> |
| #include <gui/SyncScreenCaptureListener.h> |
| #include <ui/DisplayIdentification.h> |
| #include "BackgroundExecutor.h" |
| #include "Client.h" |
| #include "ClientCache.h" |
| #include "Colorizer.h" |
| #include "DisplayDevice.h" |
| #include "DisplayHardware/ComposerHal.h" |
| #include "DisplayHardware/FramebufferSurface.h" |
| #include "DisplayHardware/HWComposer.h" |
| #include "DisplayHardware/Hal.h" |
| #include "DisplayHardware/PowerAdvisor.h" |
| #include "DisplayHardware/VirtualDisplaySurface.h" |
| #include "DisplayRenderArea.h" |
| #include "Effects/Daltonizer.h" |
| #include "FpsReporter.h" |
| #include "FrameTimeline/FrameTimeline.h" |
| #include "FrameTracer/FrameTracer.h" |
| #include "FrontEnd/LayerCreationArgs.h" |
| #include "FrontEnd/LayerHandle.h" |
| #include "FrontEnd/LayerLifecycleManager.h" |
| #include "FrontEnd/LayerLog.h" |
| #include "FrontEnd/LayerSnapshot.h" |
| #include "HdrLayerInfoReporter.h" |
| #include "Layer.h" |
| #include "LayerProtoHelper.h" |
| #include "LayerRenderArea.h" |
| #include "LayerVector.h" |
| #include "MutexUtils.h" |
| #include "NativeWindowSurface.h" |
| #include "RegionSamplingThread.h" |
| #include "RenderAreaBuilder.h" |
| #include "Scheduler/EventThread.h" |
| #include "Scheduler/LayerHistory.h" |
| #include "Scheduler/Scheduler.h" |
| #include "Scheduler/VsyncConfiguration.h" |
| #include "Scheduler/VsyncModulator.h" |
| #include "ScreenCaptureOutput.h" |
| #include "SurfaceFlingerProperties.h" |
| #include "TimeStats/TimeStats.h" |
| #include "TunnelModeEnabledReporter.h" |
| #include "Utils/Dumper.h" |
| #include "WindowInfosListenerInvoker.h" |
| |
| #include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h> |
| #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h> |
| #include <aidl/android/hardware/graphics/composer3/RenderIntent.h> |
| |
| #undef NO_THREAD_SAFETY_ANALYSIS |
| #define NO_THREAD_SAFETY_ANALYSIS \ |
| _Pragma("GCC error \"Prefer <ftl/fake_guard.h> or MutexUtils.h helpers.\"") |
| |
| namespace android { |
| using namespace std::chrono_literals; |
| using namespace std::string_literals; |
| using namespace std::string_view_literals; |
| |
| using namespace hardware::configstore; |
| using namespace hardware::configstore::V1_0; |
| using namespace sysprop; |
| using ftl::Flags; |
| using namespace ftl::flag_operators; |
| |
| using aidl::android::hardware::graphics::common::DisplayDecorationSupport; |
| using aidl::android::hardware::graphics::composer3::Capability; |
| using aidl::android::hardware::graphics::composer3::DisplayCapability; |
| using CompositionStrategyPredictionState = android::compositionengine::impl:: |
| OutputCompositionState::CompositionStrategyPredictionState; |
| |
| using base::StringAppendF; |
| using display::PhysicalDisplay; |
| using display::PhysicalDisplays; |
| using frontend::TransactionHandler; |
| using gui::DisplayInfo; |
| using gui::GameMode; |
| using gui::IDisplayEventConnection; |
| using gui::IWindowInfosListener; |
| using gui::LayerMetadata; |
| using gui::WindowInfo; |
| using gui::aidl_utils::binderStatusFromStatusT; |
| using scheduler::VsyncModulator; |
| using ui::Dataspace; |
| using ui::DisplayPrimaries; |
| using ui::RenderIntent; |
| |
| using KernelIdleTimerController = scheduler::RefreshRateSelector::KernelIdleTimerController; |
| |
| namespace hal = android::hardware::graphics::composer::hal; |
| |
| namespace { |
| |
| static constexpr int FOUR_K_WIDTH = 3840; |
| static constexpr int FOUR_K_HEIGHT = 2160; |
| |
| // TODO(b/141333600): Consolidate with DisplayMode::Builder::getDefaultDensity. |
| constexpr float FALLBACK_DENSITY = ACONFIGURATION_DENSITY_TV; |
| |
| float getDensityFromProperty(const char* property, bool required) { |
| char value[PROPERTY_VALUE_MAX]; |
| const float density = property_get(property, value, nullptr) > 0 ? std::atof(value) : 0.f; |
| if (!density && required) { |
| ALOGE("%s must be defined as a build property", property); |
| return FALLBACK_DENSITY; |
| } |
| return density; |
| } |
| |
| // Currently we only support V0_SRGB and DISPLAY_P3 as composition preference. |
| bool validateCompositionDataspace(Dataspace dataspace) { |
| return dataspace == Dataspace::V0_SRGB || dataspace == Dataspace::DISPLAY_P3; |
| } |
| |
| std::chrono::milliseconds getIdleTimerTimeout(PhysicalDisplayId displayId) { |
| if (const int32_t displayIdleTimerMs = |
| base::GetIntProperty("debug.sf.set_idle_timer_ms_"s + |
| std::to_string(displayId.value), |
| 0); |
| displayIdleTimerMs > 0) { |
| return std::chrono::milliseconds(displayIdleTimerMs); |
| } |
| |
| const int32_t setIdleTimerMs = base::GetIntProperty("debug.sf.set_idle_timer_ms"s, 0); |
| const int32_t millis = setIdleTimerMs ? setIdleTimerMs : sysprop::set_idle_timer_ms(0); |
| return std::chrono::milliseconds(millis); |
| } |
| |
| bool getKernelIdleTimerSyspropConfig(PhysicalDisplayId displayId) { |
| const bool displaySupportKernelIdleTimer = |
| base::GetBoolProperty("debug.sf.support_kernel_idle_timer_"s + |
| std::to_string(displayId.value), |
| false); |
| |
| return displaySupportKernelIdleTimer || sysprop::support_kernel_idle_timer(false); |
| } |
| |
| bool isAbove4k30(const ui::DisplayMode& outMode) { |
| using fps_approx_ops::operator>; |
| Fps refreshRate = Fps::fromValue(outMode.peakRefreshRate); |
| return outMode.resolution.getWidth() >= FOUR_K_WIDTH && |
| outMode.resolution.getHeight() >= FOUR_K_HEIGHT && refreshRate > 30_Hz; |
| } |
| |
| void excludeDolbyVisionIf4k30Present(const std::vector<ui::Hdr>& displayHdrTypes, |
| ui::DisplayMode& outMode) { |
| if (isAbove4k30(outMode) && |
| std::any_of(displayHdrTypes.begin(), displayHdrTypes.end(), |
| [](ui::Hdr type) { return type == ui::Hdr::DOLBY_VISION_4K30; })) { |
| for (ui::Hdr type : displayHdrTypes) { |
| if (type != ui::Hdr::DOLBY_VISION_4K30 && type != ui::Hdr::DOLBY_VISION) { |
| outMode.supportedHdrTypes.push_back(type); |
| } |
| } |
| } else { |
| for (ui::Hdr type : displayHdrTypes) { |
| if (type != ui::Hdr::DOLBY_VISION_4K30) { |
| outMode.supportedHdrTypes.push_back(type); |
| } |
| } |
| } |
| } |
| |
| HdrCapabilities filterOut4k30(const HdrCapabilities& displayHdrCapabilities) { |
| std::vector<ui::Hdr> hdrTypes; |
| for (ui::Hdr type : displayHdrCapabilities.getSupportedHdrTypes()) { |
| if (type != ui::Hdr::DOLBY_VISION_4K30) { |
| hdrTypes.push_back(type); |
| } |
| } |
| return {hdrTypes, displayHdrCapabilities.getDesiredMaxLuminance(), |
| displayHdrCapabilities.getDesiredMaxAverageLuminance(), |
| displayHdrCapabilities.getDesiredMinLuminance()}; |
| } |
| |
| uint32_t getLayerIdFromSurfaceControl(sp<SurfaceControl> surfaceControl) { |
| if (!surfaceControl) { |
| return UNASSIGNED_LAYER_ID; |
| } |
| return LayerHandle::getLayerId(surfaceControl->getHandle()); |
| } |
| |
| /** |
| * Returns true if the file at path exists and is newer than duration. |
| */ |
| bool fileNewerThan(const std::string& path, std::chrono::minutes duration) { |
| using Clock = std::filesystem::file_time_type::clock; |
| std::error_code error; |
| std::filesystem::file_time_type updateTime = std::filesystem::last_write_time(path, error); |
| if (error) { |
| return false; |
| } |
| return duration > (Clock::now() - updateTime); |
| } |
| |
| bool isFrameIntervalOnCadence(TimePoint expectedPresentTime, TimePoint lastExpectedPresentTimestamp, |
| Fps lastFrameInterval, Period timeout, Duration threshold) { |
| if (lastFrameInterval.getPeriodNsecs() == 0) { |
| return false; |
| } |
| |
| const auto expectedPresentTimeDeltaNs = |
| expectedPresentTime.ns() - lastExpectedPresentTimestamp.ns(); |
| |
| if (expectedPresentTimeDeltaNs > timeout.ns()) { |
| return false; |
| } |
| |
| const auto expectedPresentPeriods = static_cast<nsecs_t>( |
| std::round(static_cast<float>(expectedPresentTimeDeltaNs) / |
| static_cast<float>(lastFrameInterval.getPeriodNsecs()))); |
| const auto calculatedPeriodsOutNs = lastFrameInterval.getPeriodNsecs() * expectedPresentPeriods; |
| const auto calculatedExpectedPresentTimeNs = |
| lastExpectedPresentTimestamp.ns() + calculatedPeriodsOutNs; |
| const auto presentTimeDelta = |
| std::abs(expectedPresentTime.ns() - calculatedExpectedPresentTimeNs); |
| return presentTimeDelta < threshold.ns(); |
| } |
| |
| bool isExpectedPresentWithinTimeout(TimePoint expectedPresentTime, |
| TimePoint lastExpectedPresentTimestamp, |
| std::optional<Period> timeoutOpt, Duration threshold) { |
| if (!timeoutOpt) { |
| // Always within timeout if timeoutOpt is absent and don't send hint |
| // for the timeout |
| return true; |
| } |
| |
| if (timeoutOpt->ns() == 0) { |
| // Always outside timeout if timeoutOpt is 0 and always send |
| // the hint for the timeout. |
| return false; |
| } |
| |
| if (expectedPresentTime.ns() < lastExpectedPresentTimestamp.ns() + timeoutOpt->ns()) { |
| return true; |
| } |
| |
| // Check if within the threshold as it can be just outside the timeout |
| return std::abs(expectedPresentTime.ns() - |
| (lastExpectedPresentTimestamp.ns() + timeoutOpt->ns())) < threshold.ns(); |
| } |
| } // namespace anonymous |
| |
| // --------------------------------------------------------------------------- |
| |
| const String16 sHardwareTest("android.permission.HARDWARE_TEST"); |
| const String16 sAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER"); |
| const String16 sRotateSurfaceFlinger("android.permission.ROTATE_SURFACE_FLINGER"); |
| const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER"); |
| const String16 sControlDisplayBrightness("android.permission.CONTROL_DISPLAY_BRIGHTNESS"); |
| const String16 sDump("android.permission.DUMP"); |
| const String16 sCaptureBlackoutContent("android.permission.CAPTURE_BLACKOUT_CONTENT"); |
| const String16 sInternalSystemWindow("android.permission.INTERNAL_SYSTEM_WINDOW"); |
| const String16 sWakeupSurfaceFlinger("android.permission.WAKEUP_SURFACE_FLINGER"); |
| |
| const char* KERNEL_IDLE_TIMER_PROP = "graphics.display.kernel_idle_timer.enabled"; |
| |
| // --------------------------------------------------------------------------- |
| int64_t SurfaceFlinger::dispSyncPresentTimeOffset; |
| bool SurfaceFlinger::useHwcForRgbToYuv; |
| bool SurfaceFlinger::hasSyncFramework; |
| int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers; |
| int64_t SurfaceFlinger::minAcquiredBuffers = 1; |
| uint32_t SurfaceFlinger::maxGraphicsWidth; |
| uint32_t SurfaceFlinger::maxGraphicsHeight; |
| bool SurfaceFlinger::useContextPriority; |
| Dataspace SurfaceFlinger::defaultCompositionDataspace = Dataspace::V0_SRGB; |
| ui::PixelFormat SurfaceFlinger::defaultCompositionPixelFormat = ui::PixelFormat::RGBA_8888; |
| Dataspace SurfaceFlinger::wideColorGamutCompositionDataspace = Dataspace::V0_SRGB; |
| ui::PixelFormat SurfaceFlinger::wideColorGamutCompositionPixelFormat = ui::PixelFormat::RGBA_8888; |
| LatchUnsignaledConfig SurfaceFlinger::enableLatchUnsignaledConfig; |
| |
| std::string decodeDisplayColorSetting(DisplayColorSetting displayColorSetting) { |
| switch(displayColorSetting) { |
| case DisplayColorSetting::kManaged: |
| return std::string("Managed"); |
| case DisplayColorSetting::kUnmanaged: |
| return std::string("Unmanaged"); |
| case DisplayColorSetting::kEnhanced: |
| return std::string("Enhanced"); |
| default: |
| return std::string("Unknown ") + |
| std::to_string(static_cast<int>(displayColorSetting)); |
| } |
| } |
| |
| bool callingThreadHasPermission(const String16& permission) { |
| IPCThreadState* ipc = IPCThreadState::self(); |
| const int pid = ipc->getCallingPid(); |
| const int uid = ipc->getCallingUid(); |
| return uid == AID_GRAPHICS || uid == AID_SYSTEM || |
| PermissionCache::checkPermission(permission, pid, uid); |
| } |
| |
| ui::Transform::RotationFlags SurfaceFlinger::sActiveDisplayRotationFlags = ui::Transform::ROT_0; |
| |
| SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag) |
| : mFactory(factory), |
| mPid(getpid()), |
| mTimeStats(std::make_shared<impl::TimeStats>()), |
| mFrameTracer(mFactory.createFrameTracer()), |
| mFrameTimeline(mFactory.createFrameTimeline(mTimeStats, mPid)), |
| mCompositionEngine(mFactory.createCompositionEngine()), |
| mHwcServiceName(base::GetProperty("debug.sf.hwc_service_name"s, "default"s)), |
| mTunnelModeEnabledReporter(sp<TunnelModeEnabledReporter>::make()), |
| mEmulatedDisplayDensity(getDensityFromProperty("qemu.sf.lcd_density", false)), |
| mInternalDisplayDensity( |
| getDensityFromProperty("ro.sf.lcd_density", !mEmulatedDisplayDensity)), |
| mPowerAdvisor(std::make_unique<Hwc2::impl::PowerAdvisor>(*this)), |
| mWindowInfosListenerInvoker(sp<WindowInfosListenerInvoker>::make()), |
| mSkipPowerOnForQuiescent(base::GetBoolProperty("ro.boot.quiescent"s, false)) { |
| ALOGI("Using HWComposer service: %s", mHwcServiceName.c_str()); |
| } |
| |
| SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipInitialization) { |
| ATRACE_CALL(); |
| ALOGI("SurfaceFlinger is starting"); |
| |
| hasSyncFramework = running_without_sync_framework(true); |
| |
| dispSyncPresentTimeOffset = present_time_offset_from_vsync_ns(0); |
| |
| useHwcForRgbToYuv = force_hwc_copy_for_virtual_displays(false); |
| |
| maxFrameBufferAcquiredBuffers = max_frame_buffer_acquired_buffers(2); |
| minAcquiredBuffers = |
| SurfaceFlingerProperties::min_acquired_buffers().value_or(minAcquiredBuffers); |
| |
| maxGraphicsWidth = std::max(max_graphics_width(0), 0); |
| maxGraphicsHeight = std::max(max_graphics_height(0), 0); |
| |
| mSupportsWideColor = has_wide_color_display(false); |
| mDefaultCompositionDataspace = |
| static_cast<ui::Dataspace>(default_composition_dataspace(Dataspace::V0_SRGB)); |
| mWideColorGamutCompositionDataspace = static_cast<ui::Dataspace>(wcg_composition_dataspace( |
| mSupportsWideColor ? Dataspace::DISPLAY_P3 : Dataspace::V0_SRGB)); |
| defaultCompositionDataspace = mDefaultCompositionDataspace; |
| wideColorGamutCompositionDataspace = mWideColorGamutCompositionDataspace; |
| defaultCompositionPixelFormat = static_cast<ui::PixelFormat>( |
| default_composition_pixel_format(ui::PixelFormat::RGBA_8888)); |
| wideColorGamutCompositionPixelFormat = |
| static_cast<ui::PixelFormat>(wcg_composition_pixel_format(ui::PixelFormat::RGBA_8888)); |
| |
| mLayerCachingEnabled = |
| base::GetBoolProperty("debug.sf.enable_layer_caching"s, |
| sysprop::SurfaceFlingerProperties::enable_layer_caching() |
| .value_or(false)); |
| |
| useContextPriority = use_context_priority(true); |
| |
| mInternalDisplayPrimaries = sysprop::getDisplayNativePrimaries(); |
| |
| // debugging stuff... |
| char value[PROPERTY_VALUE_MAX]; |
| |
| property_get("ro.build.type", value, "user"); |
| mIsUserBuild = strcmp(value, "user") == 0; |
| |
| mDebugFlashDelay = base::GetUintProperty("debug.sf.showupdates"s, 0u); |
| |
| mBackpressureGpuComposition = base::GetBoolProperty("debug.sf.enable_gl_backpressure"s, true); |
| ALOGI_IF(mBackpressureGpuComposition, "Enabling backpressure for GPU composition"); |
| |
| property_get("ro.surface_flinger.supports_background_blur", value, "0"); |
| bool supportsBlurs = atoi(value); |
| mSupportsBlur = supportsBlurs; |
| ALOGI_IF(!mSupportsBlur, "Disabling blur effects, they are not supported."); |
| |
| property_get("debug.sf.luma_sampling", value, "1"); |
| mLumaSampling = atoi(value); |
| |
| property_get("debug.sf.disable_client_composition_cache", value, "0"); |
| mDisableClientCompositionCache = atoi(value); |
| |
| property_get("debug.sf.predict_hwc_composition_strategy", value, "1"); |
| mPredictCompositionStrategy = atoi(value); |
| |
| property_get("debug.sf.treat_170m_as_sRGB", value, "0"); |
| mTreat170mAsSrgb = atoi(value); |
| |
| property_get("debug.sf.dim_in_gamma_in_enhanced_screenshots", value, 0); |
| mDimInGammaSpaceForEnhancedScreenshots = atoi(value); |
| |
| mIgnoreHwcPhysicalDisplayOrientation = |
| base::GetBoolProperty("debug.sf.ignore_hwc_physical_display_orientation"s, false); |
| |
| // We should be reading 'persist.sys.sf.color_saturation' here |
| // but since /data may be encrypted, we need to wait until after vold |
| // comes online to attempt to read the property. The property is |
| // instead read after the boot animation |
| |
| if (base::GetBoolProperty("debug.sf.treble_testing_override"s, false)) { |
| // Without the override SurfaceFlinger cannot connect to HIDL |
| // services that are not listed in the manifests. Considered |
| // deriving the setting from the set service name, but it |
| // would be brittle if the name that's not 'default' is used |
| // for production purposes later on. |
| ALOGI("Enabling Treble testing override"); |
| android::hardware::details::setTrebleTestingOverride(true); |
| } |
| |
| // TODO (b/270966065) Update the HWC based refresh rate overlay to support spinner |
| mRefreshRateOverlaySpinner = property_get_bool("debug.sf.show_refresh_rate_overlay_spinner", 0); |
| mRefreshRateOverlayRenderRate = |
| property_get_bool("debug.sf.show_refresh_rate_overlay_render_rate", 0); |
| mRefreshRateOverlayShowInMiddle = |
| property_get_bool("debug.sf.show_refresh_rate_overlay_in_middle", 0); |
| |
| if (!mIsUserBuild && base::GetBoolProperty("debug.sf.enable_transaction_tracing"s, true)) { |
| mTransactionTracing.emplace(); |
| mLayerTracing.setTransactionTracing(*mTransactionTracing); |
| } |
| |
| mIgnoreHdrCameraLayers = ignore_hdr_camera_layers(false); |
| |
| mLayerLifecycleManagerEnabled = |
| base::GetBoolProperty("persist.debug.sf.enable_layer_lifecycle_manager"s, true); |
| |
| // These are set by the HWC implementation to indicate that they will use the workarounds. |
| mIsHotplugErrViaNegVsync = |
| base::GetBoolProperty("debug.sf.hwc_hotplug_error_via_neg_vsync"s, false); |
| |
| mIsHdcpViaNegVsync = base::GetBoolProperty("debug.sf.hwc_hdcp_via_neg_vsync"s, false); |
| } |
| |
| LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() { |
| if (base::GetBoolProperty("debug.sf.auto_latch_unsignaled"s, true)) { |
| return LatchUnsignaledConfig::AutoSingleLayer; |
| } |
| |
| return LatchUnsignaledConfig::Disabled; |
| } |
| |
| SurfaceFlinger::~SurfaceFlinger() = default; |
| |
| void SurfaceFlinger::binderDied(const wp<IBinder>&) { |
| // the window manager died on us. prepare its eulogy. |
| mBootFinished = false; |
| |
| static_cast<void>(mScheduler->schedule([this]() FTL_FAKE_GUARD(kMainThreadContext) { |
| // Sever the link to inputflinger since it's gone as well. |
| mInputFlinger.clear(); |
| |
| initializeDisplays(); |
| })); |
| |
| mInitBootPropsFuture.callOnce([this] { |
| return std::async(std::launch::async, &SurfaceFlinger::initBootProperties, this); |
| }); |
| |
| mInitBootPropsFuture.wait(); |
| } |
| |
| void SurfaceFlinger::run() { |
| mScheduler->run(); |
| } |
| |
| sp<IBinder> SurfaceFlinger::createVirtualDisplay(const std::string& displayName, bool isSecure, |
| const std::string& uniqueId, |
| float requestedRefreshRate) { |
| // SurfaceComposerAIDL checks for some permissions, but adding an additional check here. |
| // This is to ensure that only root, system, and graphics can request to create a secure |
| // display. Secure displays can show secure content so we add an additional restriction on it. |
| const uid_t uid = IPCThreadState::self()->getCallingUid(); |
| if (isSecure && uid != AID_ROOT && uid != AID_GRAPHICS && uid != AID_SYSTEM) { |
| ALOGE("Only privileged processes can create a secure display"); |
| return nullptr; |
| } |
| |
| class DisplayToken : public BBinder { |
| sp<SurfaceFlinger> flinger; |
| virtual ~DisplayToken() { |
| // no more references, this display must be terminated |
| Mutex::Autolock _l(flinger->mStateLock); |
| flinger->mCurrentState.displays.removeItem(wp<IBinder>::fromExisting(this)); |
| flinger->setTransactionFlags(eDisplayTransactionNeeded); |
| } |
| public: |
| explicit DisplayToken(const sp<SurfaceFlinger>& flinger) |
| : flinger(flinger) { |
| } |
| }; |
| |
| sp<BBinder> token = sp<DisplayToken>::make(sp<SurfaceFlinger>::fromExisting(this)); |
| |
| Mutex::Autolock _l(mStateLock); |
| // Display ID is assigned when virtual display is allocated by HWC. |
| DisplayDeviceState state; |
| state.isSecure = isSecure; |
| // Set display as protected when marked as secure to ensure no behavior change |
| // TODO (b/314820005): separate as a different arg when creating the display. |
| state.isProtected = isSecure; |
| state.displayName = displayName; |
| state.uniqueId = uniqueId; |
| state.requestedRefreshRate = Fps::fromValue(requestedRefreshRate); |
| mCurrentState.displays.add(token, state); |
| return token; |
| } |
| |
| status_t SurfaceFlinger::destroyVirtualDisplay(const sp<IBinder>& displayToken) { |
| Mutex::Autolock lock(mStateLock); |
| |
| const ssize_t index = mCurrentState.displays.indexOfKey(displayToken); |
| if (index < 0) { |
| ALOGE("%s: Invalid display token %p", __func__, displayToken.get()); |
| return NAME_NOT_FOUND; |
| } |
| |
| const DisplayDeviceState& state = mCurrentState.displays.valueAt(index); |
| if (state.physical) { |
| ALOGE("%s: Invalid operation on physical display", __func__); |
| return INVALID_OPERATION; |
| } |
| mCurrentState.displays.removeItemsAt(index); |
| setTransactionFlags(eDisplayTransactionNeeded); |
| return NO_ERROR; |
| } |
| |
| void SurfaceFlinger::enableHalVirtualDisplays(bool enable) { |
| auto& generator = mVirtualDisplayIdGenerators.hal; |
| if (!generator && enable) { |
| ALOGI("Enabling HAL virtual displays"); |
| generator.emplace(getHwComposer().getMaxVirtualDisplayCount()); |
| } else if (generator && !enable) { |
| ALOGW_IF(generator->inUse(), "Disabling HAL virtual displays while in use"); |
| generator.reset(); |
| } |
| } |
| |
| VirtualDisplayId SurfaceFlinger::acquireVirtualDisplay(ui::Size resolution, |
| ui::PixelFormat format) { |
| if (auto& generator = mVirtualDisplayIdGenerators.hal) { |
| if (const auto id = generator->generateId()) { |
| if (getHwComposer().allocateVirtualDisplay(*id, resolution, &format)) { |
| return *id; |
| } |
| |
| generator->releaseId(*id); |
| } else { |
| ALOGW("%s: Exhausted HAL virtual displays", __func__); |
| } |
| |
| ALOGW("%s: Falling back to GPU virtual display", __func__); |
| } |
| |
| const auto id = mVirtualDisplayIdGenerators.gpu.generateId(); |
| LOG_ALWAYS_FATAL_IF(!id, "Failed to generate ID for GPU virtual display"); |
| return *id; |
| } |
| |
| void SurfaceFlinger::releaseVirtualDisplay(VirtualDisplayId displayId) { |
| if (const auto id = HalVirtualDisplayId::tryCast(displayId)) { |
| if (auto& generator = mVirtualDisplayIdGenerators.hal) { |
| generator->releaseId(*id); |
| } |
| return; |
| } |
| |
| const auto id = GpuVirtualDisplayId::tryCast(displayId); |
| LOG_ALWAYS_FATAL_IF(!id); |
| mVirtualDisplayIdGenerators.gpu.releaseId(*id); |
| } |
| |
| std::vector<PhysicalDisplayId> SurfaceFlinger::getPhysicalDisplayIdsLocked() const { |
| std::vector<PhysicalDisplayId> displayIds; |
| displayIds.reserve(mPhysicalDisplays.size()); |
| |
| const auto defaultDisplayId = getDefaultDisplayDeviceLocked()->getPhysicalId(); |
| displayIds.push_back(defaultDisplayId); |
| |
| for (const auto& [id, display] : mPhysicalDisplays) { |
| if (id != defaultDisplayId) { |
| displayIds.push_back(id); |
| } |
| } |
| |
| return displayIds; |
| } |
| |
| std::optional<PhysicalDisplayId> SurfaceFlinger::getPhysicalDisplayIdLocked( |
| const sp<display::DisplayToken>& displayToken) const { |
| return ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken)) |
| .transform(&ftl::to_key<PhysicalDisplays>); |
| } |
| |
| sp<IBinder> SurfaceFlinger::getPhysicalDisplayToken(PhysicalDisplayId displayId) const { |
| Mutex::Autolock lock(mStateLock); |
| return getPhysicalDisplayTokenLocked(displayId); |
| } |
| |
| HWComposer& SurfaceFlinger::getHwComposer() const { |
| return mCompositionEngine->getHwComposer(); |
| } |
| |
| renderengine::RenderEngine& SurfaceFlinger::getRenderEngine() const { |
| return *mRenderEngine; |
| } |
| |
| compositionengine::CompositionEngine& SurfaceFlinger::getCompositionEngine() const { |
| return *mCompositionEngine.get(); |
| } |
| |
| void SurfaceFlinger::bootFinished() { |
| if (mBootFinished == true) { |
| ALOGE("Extra call to bootFinished"); |
| return; |
| } |
| mBootFinished = true; |
| FlagManager::getMutableInstance().markBootCompleted(); |
| |
| mInitBootPropsFuture.wait(); |
| mRenderEnginePrimeCacheFuture.wait(); |
| |
| const nsecs_t now = systemTime(); |
| const nsecs_t duration = now - mBootTime; |
| ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) ); |
| |
| mFrameTracer->initialize(); |
| mFrameTimeline->onBootFinished(); |
| getRenderEngine().setEnableTracing(FlagManager::getInstance().use_skia_tracing()); |
| |
| // wait patiently for the window manager death |
| const String16 name("window"); |
| mWindowManager = defaultServiceManager()->waitForService(name); |
| if (mWindowManager != 0) { |
| mWindowManager->linkToDeath(sp<IBinder::DeathRecipient>::fromExisting(this)); |
| } |
| |
| // stop boot animation |
| // formerly we would just kill the process, but we now ask it to exit so it |
| // can choose where to stop the animation. |
| property_set("service.bootanim.exit", "1"); |
| |
| const int LOGTAG_SF_STOP_BOOTANIM = 60110; |
| LOG_EVENT_LONG(LOGTAG_SF_STOP_BOOTANIM, |
| ns2ms(systemTime(SYSTEM_TIME_MONOTONIC))); |
| |
| sp<IBinder> input(defaultServiceManager()->waitForService(String16("inputflinger"))); |
| |
| static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) { |
| if (input == nullptr) { |
| ALOGE("Failed to link to input service"); |
| } else { |
| mInputFlinger = interface_cast<os::IInputFlinger>(input); |
| } |
| |
| readPersistentProperties(); |
| const bool hintSessionEnabled = FlagManager::getInstance().use_adpf_cpu_hint(); |
| mPowerAdvisor->enablePowerHintSession(hintSessionEnabled); |
| const bool hintSessionUsed = mPowerAdvisor->usePowerHintSession(); |
| // Ordering is important here, as onBootFinished signals to PowerAdvisor that concurrency |
| // is safe because its variables are initialized. |
| mPowerAdvisor->onBootFinished(); |
| ALOGD("Power hint is %s", |
| hintSessionUsed ? "supported" : (hintSessionEnabled ? "unsupported" : "disabled")); |
| if (hintSessionUsed) { |
| std::optional<pid_t> renderEngineTid = getRenderEngine().getRenderEngineTid(); |
| std::vector<int32_t> tidList; |
| tidList.emplace_back(gettid()); |
| if (renderEngineTid.has_value()) { |
| tidList.emplace_back(*renderEngineTid); |
| } |
| if (!mPowerAdvisor->startPowerHintSession(std::move(tidList))) { |
| ALOGW("Cannot start power hint session"); |
| } |
| } |
| |
| mBootStage = BootStage::FINISHED; |
| |
| if (base::GetBoolProperty("sf.debug.show_refresh_rate_overlay"s, false)) { |
| ftl::FakeGuard guard(mStateLock); |
| enableRefreshRateOverlay(true); |
| } |
| })); |
| } |
| |
| void chooseRenderEngineType(renderengine::RenderEngineCreationArgs::Builder& builder) { |
| char prop[PROPERTY_VALUE_MAX]; |
| property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, ""); |
| |
| // TODO: b/293371537 - Once GraphiteVk is deemed relatively stable, log a warning that |
| // PROPERTY_DEBUG_RENDERENGINE_BACKEND is deprecated |
| if (strcmp(prop, "skiagl") == 0) { |
| builder.setThreaded(renderengine::RenderEngine::Threaded::NO) |
| .setGraphicsApi(renderengine::RenderEngine::GraphicsApi::GL); |
| } else if (strcmp(prop, "skiaglthreaded") == 0) { |
| builder.setThreaded(renderengine::RenderEngine::Threaded::YES) |
| .setGraphicsApi(renderengine::RenderEngine::GraphicsApi::GL); |
| } else if (strcmp(prop, "skiavk") == 0) { |
| builder.setThreaded(renderengine::RenderEngine::Threaded::NO) |
| .setGraphicsApi(renderengine::RenderEngine::GraphicsApi::VK); |
| } else if (strcmp(prop, "skiavkthreaded") == 0) { |
| builder.setThreaded(renderengine::RenderEngine::Threaded::YES) |
| .setGraphicsApi(renderengine::RenderEngine::GraphicsApi::VK); |
| } else { |
| const auto kVulkan = renderengine::RenderEngine::GraphicsApi::VK; |
| // TODO: b/341728634 - Clean up conditional compilation. |
| // Note: this guard in particular must check e.g. |
| // COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS_GRAPHITE_RENDERENGINE directly (instead of calling e.g. |
| // COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS(GRAPHITE_RENDERENGINE)) because that macro is undefined |
| // in the libsurfaceflingerflags_test variant of com_android_graphics_surfaceflinger_flags.h, which |
| // is used by layertracegenerator (which also needs SurfaceFlinger.cpp). :) |
| #if COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS_GRAPHITE_RENDERENGINE || \ |
| COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS_FORCE_COMPILE_GRAPHITE_RENDERENGINE |
| const bool useGraphite = FlagManager::getInstance().graphite_renderengine() && |
| renderengine::RenderEngine::canSupport(kVulkan); |
| #else |
| const bool useGraphite = false; |
| if (FlagManager::getInstance().graphite_renderengine()) { |
| ALOGE("RenderEngine's Graphite Skia backend was requested with the " |
| "debug.renderengine.graphite system property, but it is not compiled in this " |
| "build! Falling back to Ganesh backend selection logic."); |
| } |
| #endif |
| const bool useVulkan = useGraphite || |
| (FlagManager::getInstance().vulkan_renderengine() && |
| renderengine::RenderEngine::canSupport(kVulkan)); |
| |
| builder.setSkiaBackend(useGraphite ? renderengine::RenderEngine::SkiaBackend::GRAPHITE |
| : renderengine::RenderEngine::SkiaBackend::GANESH); |
| builder.setGraphicsApi(useVulkan ? kVulkan : renderengine::RenderEngine::GraphicsApi::GL); |
| } |
| } |
| |
| /** |
| * Choose a suggested blurring algorithm if supportsBlur is true. By default Kawase will be |
| * suggested as it's faster than a full Gaussian blur and looks close enough. |
| */ |
| renderengine::RenderEngine::BlurAlgorithm chooseBlurAlgorithm(bool supportsBlur) { |
| if (!supportsBlur) { |
| return renderengine::RenderEngine::BlurAlgorithm::NONE; |
| } |
| |
| auto const algorithm = base::GetProperty(PROPERTY_DEBUG_RENDERENGINE_BLUR_ALGORITHM, ""); |
| if (algorithm == "gaussian") { |
| return renderengine::RenderEngine::BlurAlgorithm::GAUSSIAN; |
| } else { |
| return renderengine::RenderEngine::BlurAlgorithm::KAWASE; |
| } |
| } |
| |
| void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) { |
| ATRACE_CALL(); |
| ALOGI( "SurfaceFlinger's main thread ready to run. " |
| "Initializing graphics H/W..."); |
| addTransactionReadyFilters(); |
| Mutex::Autolock lock(mStateLock); |
| |
| // Get a RenderEngine for the given display / config (can't fail) |
| // TODO(b/77156734): We need to stop casting and use HAL types when possible. |
| // Sending maxFrameBufferAcquiredBuffers as the cache size is tightly tuned to single-display. |
| auto builder = renderengine::RenderEngineCreationArgs::Builder() |
| .setPixelFormat(static_cast<int32_t>(defaultCompositionPixelFormat)) |
| .setImageCacheSize(maxFrameBufferAcquiredBuffers) |
| .setEnableProtectedContext(enable_protected_contents(false)) |
| .setPrecacheToneMapperShaderOnly(false) |
| .setBlurAlgorithm(chooseBlurAlgorithm(mSupportsBlur)) |
| .setContextPriority( |
| useContextPriority |
| ? renderengine::RenderEngine::ContextPriority::REALTIME |
| : renderengine::RenderEngine::ContextPriority::MEDIUM); |
| chooseRenderEngineType(builder); |
| mRenderEngine = renderengine::RenderEngine::create(builder.build()); |
| mCompositionEngine->setRenderEngine(mRenderEngine.get()); |
| mMaxRenderTargetSize = |
| std::min(getRenderEngine().getMaxTextureSize(), getRenderEngine().getMaxViewportDims()); |
| |
| // Set SF main policy after initializing RenderEngine which has its own policy. |
| if (!SetTaskProfiles(0, {"SFMainPolicy"})) { |
| ALOGW("Failed to set main task profile"); |
| } |
| |
| mCompositionEngine->setTimeStats(mTimeStats); |
| |
| mCompositionEngine->setHwComposer(getFactory().createHWComposer(mHwcServiceName)); |
| auto& composer = mCompositionEngine->getHwComposer(); |
| composer.setCallback(*this); |
| mDisplayModeController.setHwComposer(&composer); |
| |
| ClientCache::getInstance().setRenderEngine(&getRenderEngine()); |
| |
| mHasReliablePresentFences = |
| !getHwComposer().hasCapability(Capability::PRESENT_FENCE_IS_NOT_RELIABLE); |
| |
| enableLatchUnsignaledConfig = getLatchUnsignaledConfig(); |
| |
| if (base::GetBoolProperty("debug.sf.enable_hwc_vds"s, false)) { |
| enableHalVirtualDisplays(true); |
| } |
| |
| // Process hotplug for displays connected at boot. |
| LOG_ALWAYS_FATAL_IF(!configureLocked(), |
| "Initial display configuration failed: HWC did not hotplug"); |
| |
| // Commit primary display. |
| sp<const DisplayDevice> display; |
| if (const auto indexOpt = mCurrentState.getDisplayIndex(getPrimaryDisplayIdLocked())) { |
| const auto& displays = mCurrentState.displays; |
| |
| const auto& token = displays.keyAt(*indexOpt); |
| const auto& state = displays.valueAt(*indexOpt); |
| |
| processDisplayAdded(token, state); |
| mDrawingState.displays.add(token, state); |
| |
| display = getDefaultDisplayDeviceLocked(); |
| } |
| |
| LOG_ALWAYS_FATAL_IF(!display, "Failed to configure the primary display"); |
| LOG_ALWAYS_FATAL_IF(!getHwComposer().isConnected(display->getPhysicalId()), |
| "Primary display is disconnected"); |
| |
| // TODO(b/241285876): The Scheduler needlessly depends on creating the CompositionEngine part of |
| // the DisplayDevice, hence the above commit of the primary display. Remove that special case by |
| // initializing the Scheduler after configureLocked, once decoupled from DisplayDevice. |
| initScheduler(display); |
| |
| // Start listening after creating the Scheduler, since the listener calls into it. |
| mDisplayModeController.setActiveModeListener( |
| display::DisplayModeController::ActiveModeListener::make( |
| [this](PhysicalDisplayId displayId, Fps vsyncRate, Fps renderRate) { |
| // This callback cannot lock mStateLock, as some callers already lock it. |
| // Instead, switch context to the main thread. |
| static_cast<void>(mScheduler->schedule([=, |
| this]() FTL_FAKE_GUARD(mStateLock) { |
| if (const auto display = getDisplayDeviceLocked(displayId)) { |
| display->updateRefreshRateOverlayRate(vsyncRate, renderRate); |
| } |
| })); |
| })); |
| |
| mLayerTracing.setTakeLayersSnapshotProtoFunction([&](uint32_t traceFlags) { |
| auto snapshot = perfetto::protos::LayersSnapshotProto{}; |
| mScheduler |
| ->schedule([&]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(kMainThreadContext) { |
| snapshot = takeLayersSnapshotProto(traceFlags, TimePoint::now(), |
| mLastCommittedVsyncId, true); |
| }) |
| .wait(); |
| return snapshot; |
| }); |
| |
| // Commit secondary display(s). |
| processDisplayChangesLocked(); |
| |
| // initialize our drawing state |
| mDrawingState = mCurrentState; |
| |
| onActiveDisplayChangedLocked(nullptr, *display); |
| |
| static_cast<void>(mScheduler->schedule( |
| [this]() FTL_FAKE_GUARD(kMainThreadContext) { initializeDisplays(); })); |
| |
| mPowerAdvisor->init(); |
| |
| if (base::GetBoolProperty("service.sf.prime_shader_cache"s, true)) { |
| if (setSchedFifo(false) != NO_ERROR) { |
| ALOGW("Can't set SCHED_OTHER for primeCache"); |
| } |
| |
| mRenderEnginePrimeCacheFuture.callOnce([this] { |
| renderengine::PrimeCacheConfig config; |
| config.cacheHolePunchLayer = |
| base::GetBoolProperty("debug.sf.prime_shader_cache.hole_punch"s, true); |
| config.cacheSolidLayers = |
| base::GetBoolProperty("debug.sf.prime_shader_cache.solid_layers"s, true); |
| config.cacheSolidDimmedLayers = |
| base::GetBoolProperty("debug.sf.prime_shader_cache.solid_dimmed_layers"s, true); |
| config.cacheImageLayers = |
| base::GetBoolProperty("debug.sf.prime_shader_cache.image_layers"s, true); |
| config.cacheImageDimmedLayers = |
| base::GetBoolProperty("debug.sf.prime_shader_cache.image_dimmed_layers"s, true); |
| config.cacheClippedLayers = |
| base::GetBoolProperty("debug.sf.prime_shader_cache.clipped_layers"s, true); |
| config.cacheShadowLayers = |
| base::GetBoolProperty("debug.sf.prime_shader_cache.shadow_layers"s, true); |
| config.cachePIPImageLayers = |
| base::GetBoolProperty("debug.sf.prime_shader_cache.pip_image_layers"s, true); |
| config.cacheTransparentImageDimmedLayers = base:: |
| GetBoolProperty("debug.sf.prime_shader_cache.transparent_image_dimmed_layers"s, |
| true); |
| config.cacheClippedDimmedImageLayers = base:: |
| GetBoolProperty("debug.sf.prime_shader_cache.clipped_dimmed_image_layers"s, |
| true); |
| // ro.surface_flinger.prime_chader_cache.ultrahdr exists as a previous ro property |
| // which we maintain for backwards compatibility. |
| config.cacheUltraHDR = |
| base::GetBoolProperty("ro.surface_flinger.prime_shader_cache.ultrahdr"s, false); |
| return getRenderEngine().primeCache(config); |
| }); |
| |
| if (setSchedFifo(true) != NO_ERROR) { |
| ALOGW("Can't set SCHED_FIFO after primeCache"); |
| } |
| } |
| |
| // Avoid blocking the main thread on `init` to set properties. |
| mInitBootPropsFuture.callOnce([this] { |
| return std::async(std::launch::async, &SurfaceFlinger::initBootProperties, this); |
| }); |
| |
| initTransactionTraceWriter(); |
| ALOGV("Done initializing"); |
| } |
| |
| // During boot, offload `initBootProperties` to another thread. `property_set` depends on |
| // `property_service`, which may be delayed by slow operations like `mount_all --late` in |
| // the `init` process. See b/34499826 and b/63844978. |
| void SurfaceFlinger::initBootProperties() { |
| property_set("service.sf.present_timestamp", mHasReliablePresentFences ? "1" : "0"); |
| |
| if (base::GetBoolProperty("debug.sf.boot_animation"s, true)) { |
| // Reset and (if needed) start BootAnimation. |
| property_set("service.bootanim.exit", "0"); |
| property_set("service.bootanim.progress", "0"); |
| property_set("ctl.start", "bootanim"); |
| } |
| } |
| |
| void SurfaceFlinger::initTransactionTraceWriter() { |
| if (!mTransactionTracing) { |
| return; |
| } |
| TransactionTraceWriter::getInstance().setWriterFunction( |
| [&](const std::string& filename, bool overwrite) { |
| auto writeFn = [&]() { |
| if (!overwrite && fileNewerThan(filename, std::chrono::minutes{10})) { |
| ALOGD("TransactionTraceWriter: file=%s already exists", filename.c_str()); |
| return; |
| } |
| ALOGD("TransactionTraceWriter: writing file=%s", filename.c_str()); |
| mTransactionTracing->writeToFile(filename); |
| mTransactionTracing->flush(); |
| }; |
| if (std::this_thread::get_id() == mMainThreadId) { |
| writeFn(); |
| } else { |
| mScheduler->schedule(writeFn).get(); |
| } |
| }); |
| } |
| |
| void SurfaceFlinger::readPersistentProperties() { |
| Mutex::Autolock _l(mStateLock); |
| |
| char value[PROPERTY_VALUE_MAX]; |
| |
| property_get("persist.sys.sf.color_saturation", value, "1.0"); |
| mGlobalSaturationFactor = atof(value); |
| updateColorMatrixLocked(); |
| ALOGV("Saturation is set to %.2f", mGlobalSaturationFactor); |
| |
| property_get("persist.sys.sf.native_mode", value, "0"); |
| mDisplayColorSetting = static_cast<DisplayColorSetting>(atoi(value)); |
| |
| mForceColorMode = |
| static_cast<ui::ColorMode>(base::GetIntProperty("persist.sys.sf.color_mode"s, 0)); |
| } |
| |
| status_t SurfaceFlinger::getSupportedFrameTimestamps( |
| std::vector<FrameEvent>* outSupported) const { |
| *outSupported = { |
| FrameEvent::REQUESTED_PRESENT, |
| FrameEvent::ACQUIRE, |
| FrameEvent::LATCH, |
| FrameEvent::FIRST_REFRESH_START, |
| FrameEvent::LAST_REFRESH_START, |
| FrameEvent::GPU_COMPOSITION_DONE, |
| FrameEvent::DEQUEUE_READY, |
| FrameEvent::RELEASE, |
| }; |
| |
| if (mHasReliablePresentFences) { |
| outSupported->push_back(FrameEvent::DISPLAY_PRESENT); |
| } |
| return NO_ERROR; |
| } |
| |
| status_t SurfaceFlinger::getDisplayState(const sp<IBinder>& displayToken, ui::DisplayState* state) { |
| if (!displayToken || !state) { |
| return BAD_VALUE; |
| } |
| |
| Mutex::Autolock lock(mStateLock); |
| |
| const auto display = getDisplayDeviceLocked(displayToken); |
| if (!display) { |
| return NAME_NOT_FOUND; |
| } |
| |
| state->layerStack = display->getLayerStack(); |
| state->orientation = display->getOrientation(); |
| |
| const Rect layerStackRect = display->getLayerStackSpaceRect(); |
| state->layerStackSpaceRect = |
| layerStackRect.isValid() ? layerStackRect.getSize() : display->getSize(); |
| |
| return NO_ERROR; |
| } |
| |
| status_t SurfaceFlinger::getStaticDisplayInfo(int64_t displayId, ui::StaticDisplayInfo* info) { |
| if (!info) { |
| return BAD_VALUE; |
| } |
| |
| Mutex::Autolock lock(mStateLock); |
| const auto id = DisplayId::fromValue<PhysicalDisplayId>(static_cast<uint64_t>(displayId)); |
| const auto displayOpt = mPhysicalDisplays.get(*id).and_then(getDisplayDeviceAndSnapshot()); |
| |
| if (!displayOpt) { |
| return NAME_NOT_FOUND; |
| } |
| |
| const auto& [display, snapshotRef] = *displayOpt; |
| const auto& snapshot = snapshotRef.get(); |
| |
| info->connectionType = snapshot.connectionType(); |
| info->deviceProductInfo = snapshot.deviceProductInfo(); |
| |
| if (mEmulatedDisplayDensity) { |
| info->density = mEmulatedDisplayDensity; |
| } else { |
| info->density = info->connectionType == ui::DisplayConnectionType::Internal |
| ? mInternalDisplayDensity |
| : FALLBACK_DENSITY; |
| } |
| info->density /= ACONFIGURATION_DENSITY_MEDIUM; |
| |
| info->secure = display->isSecure(); |
| info->installOrientation = display->getPhysicalOrientation(); |
| |
| return NO_ERROR; |
| } |
| |
| void SurfaceFlinger::getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo*& info, |
| const sp<DisplayDevice>& display, |
| const display::DisplaySnapshot& snapshot) { |
| const auto& displayModes = snapshot.displayModes(); |
| info->supportedDisplayModes.clear(); |
| info->supportedDisplayModes.reserve(displayModes.size()); |
| |
| for (const auto& [id, mode] : displayModes) { |
| ui::DisplayMode outMode; |
| outMode.id = ftl::to_underlying(id); |
| |
| auto [width, height] = mode->getResolution(); |
| auto [xDpi, yDpi] = mode->getDpi(); |
| |
| if (const auto physicalOrientation = display->getPhysicalOrientation(); |
| physicalOrientation == ui::ROTATION_90 || physicalOrientation == ui::ROTATION_270) { |
| std::swap(width, height); |
| std::swap(xDpi, yDpi); |
| } |
| |
| outMode.resolution = ui::Size(width, height); |
| |
| outMode.xDpi = xDpi; |
| outMode.yDpi = yDpi; |
| |
| const auto peakFps = mode->getPeakFps(); |
| outMode.peakRefreshRate = peakFps.getValue(); |
| outMode.vsyncRate = mode->getVsyncRate().getValue(); |
| |
| const auto vsyncConfigSet = mScheduler->getVsyncConfiguration().getConfigsForRefreshRate( |
| Fps::fromValue(outMode.peakRefreshRate)); |
| outMode.appVsyncOffset = vsyncConfigSet.late.appOffset; |
| outMode.sfVsyncOffset = vsyncConfigSet.late.sfOffset; |
| outMode.group = mode->getGroup(); |
| |
| // This is how far in advance a buffer must be queued for |
| // presentation at a given time. If you want a buffer to appear |
| // on the screen at time N, you must submit the buffer before |
| // (N - presentationDeadline). |
| // |
| // Normally it's one full refresh period (to give SF a chance to |
| // latch the buffer), but this can be reduced by configuring a |
| // VsyncController offset. Any additional delays introduced by the hardware |
| // composer or panel must be accounted for here. |
| // |
| // We add an additional 1ms to allow for processing time and |
| // differences between the ideal and actual refresh rate. |
| outMode.presentationDeadline = peakFps.getPeriodNsecs() - outMode.sfVsyncOffset + 1000000; |
| excludeDolbyVisionIf4k30Present(display->getHdrCapabilities().getSupportedHdrTypes(), |
| outMode); |
| info->supportedDisplayModes.push_back(outMode); |
| } |
| |
| info->supportedColorModes = snapshot.filterColorModes(mSupportsWideColor); |
| |
| const PhysicalDisplayId displayId = snapshot.displayId(); |
| |
| const auto mode = display->refreshRateSelector().getActiveMode(); |
| info->activeDisplayModeId = ftl::to_underlying(mode.modePtr->getId()); |
| info->renderFrameRate = mode.fps.getValue(); |
| info->activeColorMode = display->getCompositionDisplay()->getState().colorMode; |
| info->hdrCapabilities = filterOut4k30(display->getHdrCapabilities()); |
| |
| info->autoLowLatencyModeSupported = |
| getHwComposer().hasDisplayCapability(displayId, |
| DisplayCapability::AUTO_LOW_LATENCY_MODE); |
| info->gameContentTypeSupported = |
| getHwComposer().supportsContentType(displayId, hal::ContentType::GAME); |
| |
| info->preferredBootDisplayMode = static_cast<ui::DisplayModeId>(-1); |
| |
| if (getHwComposer().hasCapability(Capability::BOOT_DISPLAY_CONFIG)) { |
| if (const auto hwcId = getHwComposer().getPreferredBootDisplayMode(displayId)) { |
| if (const auto modeId = snapshot.translateModeId(*hwcId)) { |
| info->preferredBootDisplayMode = ftl::to_underlying(*modeId); |
| } |
| } |
| } |
| } |
| |
| status_t SurfaceFlinger::getDynamicDisplayInfoFromId(int64_t physicalDisplayId, |
| ui::DynamicDisplayInfo* info) { |
| if (!info) { |
| return BAD_VALUE; |
| } |
| |
| Mutex::Autolock lock(mStateLock); |
| |
| const auto id_ = |
| DisplayId::fromValue<PhysicalDisplayId>(static_cast<uint64_t>(physicalDisplayId)); |
| const auto displayOpt = mPhysicalDisplays.get(*id_).and_then(getDisplayDeviceAndSnapshot()); |
| |
| if (!displayOpt) { |
| return NAME_NOT_FOUND; |
| } |
| |
| const auto& [display, snapshotRef] = *displayOpt; |
| getDynamicDisplayInfoInternal(info, display, snapshotRef.get()); |
| return NO_ERROR; |
| } |
| |
| status_t SurfaceFlinger::getDynamicDisplayInfoFromToken(const sp<IBinder>& displayToken, |
| ui::DynamicDisplayInfo* info) { |
| if (!displayToken || !info) { |
| return BAD_VALUE; |
| } |
| |
| Mutex::Autolock lock(mStateLock); |
| |
| const auto displayOpt = ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken)) |
| .transform(&ftl::to_mapped_ref<PhysicalDisplays>) |
| .and_then(getDisplayDeviceAndSnapshot()); |
| |
| if (!displayOpt) { |
| return NAME_NOT_FOUND; |
| } |
| |
| const auto& [display, snapshotRef] = *displayOpt; |
| getDynamicDisplayInfoInternal(info, display, snapshotRef.get()); |
| return NO_ERROR; |
| } |
| |
| status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>& displayToken, |
| DisplayStatInfo* outStats) { |
| if (!outStats) { |
| return BAD_VALUE; |
| } |
| |
| std::optional<PhysicalDisplayId> displayIdOpt; |
| { |
| Mutex::Autolock lock(mStateLock); |
| if (displayToken) { |
| displayIdOpt = getPhysicalDisplayIdLocked(displayToken); |
| if (!displayIdOpt) { |
| ALOGW("%s: Invalid physical display token %p", __func__, displayToken.get()); |
| return NAME_NOT_FOUND; |
| } |
| } else { |
| // TODO (b/277364366): Clients should be updated to pass in the display they |
| // want, rather than us picking an arbitrary one (the active display, in this |
| // case). |
| displayIdOpt = mActiveDisplayId; |
| } |
| } |
| |
| const auto schedule = mScheduler->getVsyncSchedule(displayIdOpt); |
| if (!schedule) { |
| ALOGE("%s: Missing VSYNC schedule for display %s!", __func__, |
| to_string(*displayIdOpt).c_str()); |
| return NAME_NOT_FOUND; |
| } |
| outStats->vsyncTime = schedule->vsyncDeadlineAfter(TimePoint::now()).ns(); |
| outStats->vsyncPeriod = schedule->period().ns(); |
| return NO_ERROR; |
| } |
| |
| void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& desiredMode) { |
| const auto mode = desiredMode.mode; |
| const auto displayId = mode.modePtr->getPhysicalDisplayId(); |
| |
| ATRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str()); |
| |
| const bool emitEvent = desiredMode.emitEvent; |
| |
| using DesiredModeAction = display::DisplayModeController::DesiredModeAction; |
| |
| switch (mDisplayModeController.setDesiredMode(displayId, std::move(desiredMode))) { |
| case DesiredModeAction::InitiateDisplayModeSwitch: { |
| const auto selectorPtr = mDisplayModeController.selectorPtrFor(displayId); |
| if (!selectorPtr) break; |
| |
| const Fps renderRate = selectorPtr->getActiveMode().fps; |
| |
| // DisplayModeController::setDesiredMode updated the render rate, so inform Scheduler. |
| mScheduler->setRenderRate(displayId, renderRate, true /* applyImmediately */); |
| |
| // Schedule a new frame to initiate the display mode switch. |
| scheduleComposite(FrameHint::kNone); |
| |
| // Start receiving vsync samples now, so that we can detect a period |
| // switch. |
| mScheduler->resyncToHardwareVsync(displayId, true /* allowToEnable */, |
| mode.modePtr.get()); |
| |
| // As we called to set period, we will call to onRefreshRateChangeCompleted once |
| // VsyncController model is locked. |
| mScheduler->modulateVsync(displayId, &VsyncModulator::onRefreshRateChangeInitiated); |
| |
| if (displayId == mActiveDisplayId) { |
| mScheduler->updatePhaseConfiguration(mode.fps); |
| } |
| |
| mScheduler->setModeChangePending(true); |
| break; |
| } |
| case DesiredModeAction::InitiateRenderRateSwitch: |
| mScheduler->setRenderRate(displayId, mode.fps, /*applyImmediately*/ false); |
| |
| if (displayId == mActiveDisplayId) { |
| mScheduler->updatePhaseConfiguration(mode.fps); |
| } |
| |
| if (emitEvent) { |
| dispatchDisplayModeChangeEvent(displayId, mode); |
| } |
| break; |
| case DesiredModeAction::None: |
| break; |
| } |
| } |
| |
| status_t SurfaceFlinger::setActiveModeFromBackdoor(const sp<display::DisplayToken>& displayToken, |
| DisplayModeId modeId, Fps minFps, Fps maxFps) { |
| ATRACE_CALL(); |
| |
| if (!displayToken) { |
| return BAD_VALUE; |
| } |
| |
| const char* const whence = __func__; |
| auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) -> status_t { |
| const auto displayOpt = |
| FTL_FAKE_GUARD(mStateLock, |
| ftl::find_if(mPhysicalDisplays, |
| PhysicalDisplay::hasToken(displayToken)) |
| .transform(&ftl::to_mapped_ref<PhysicalDisplays>) |
| .and_then(getDisplayDeviceAndSnapshot())); |
| if (!displayOpt) { |
| ALOGE("%s: Invalid physical display token %p", whence, displayToken.get()); |
| return NAME_NOT_FOUND; |
| } |
| |
| const auto& [display, snapshotRef] = *displayOpt; |
| const auto& snapshot = snapshotRef.get(); |
| |
| const auto fpsOpt = snapshot.displayModes().get(modeId).transform( |
| [](const DisplayModePtr& mode) { return mode->getPeakFps(); }); |
| |
| if (!fpsOpt) { |
| ALOGE("%s: Invalid mode %d for display %s", whence, ftl::to_underlying(modeId), |
| to_string(snapshot.displayId()).c_str()); |
| return BAD_VALUE; |
| } |
| |
| const Fps fps = *fpsOpt; |
| const FpsRange physical = {fps, fps}; |
| const FpsRange render = {minFps.isValid() ? minFps : fps, maxFps.isValid() ? maxFps : fps}; |
| const FpsRanges ranges = {physical, render}; |
| |
| // Keep the old switching type. |
| const bool allowGroupSwitching = |
| display->refreshRateSelector().getCurrentPolicy().allowGroupSwitching; |
| |
| const scheduler::RefreshRateSelector::DisplayManagerPolicy policy{modeId, ranges, ranges, |
| allowGroupSwitching}; |
| |
| return setDesiredDisplayModeSpecsInternal(display, policy); |
| }); |
| |
| return future.get(); |
| } |
| |
| // TODO: b/241285876 - Restore thread safety analysis once mStateLock below is unconditional. |
| [[clang::no_thread_safety_analysis]] |
| void SurfaceFlinger::finalizeDisplayModeChange(PhysicalDisplayId displayId) { |
| ATRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str()); |
| |
| const auto pendingModeOpt = mDisplayModeController.getPendingMode(displayId); |
| if (!pendingModeOpt) { |
| // There is no pending mode change. This can happen if the active |
| // display changed and the mode change happened on a different display. |
| return; |
| } |
| |
| const auto& activeMode = pendingModeOpt->mode; |
| |
| if (const auto oldResolution = |
| mDisplayModeController.getActiveMode(displayId).modePtr->getResolution(); |
| oldResolution != activeMode.modePtr->getResolution()) { |
| ConditionalLock lock(mStateLock, !FlagManager::getInstance().connected_display()); |
| |
| auto& state = mCurrentState.displays.editValueFor(getPhysicalDisplayTokenLocked(displayId)); |
| // We need to generate new sequenceId in order to recreate the display (and this |
| // way the framebuffer). |
| state.sequenceId = DisplayDeviceState{}.sequenceId; |
| state.physical->activeMode = activeMode.modePtr.get(); |
| processDisplayChangesLocked(); |
| |
| // processDisplayChangesLocked will update all necessary components so we're done here. |
| return; |
| } |
| |
| mDisplayModeController.finalizeModeChange(displayId, activeMode.modePtr->getId(), |
| activeMode.modePtr->getVsyncRate(), activeMode.fps); |
| |
| if (displayId == mActiveDisplayId) { |
| mScheduler->updatePhaseConfiguration(activeMode.fps); |
| } |
| |
| if (pendingModeOpt->emitEvent) { |
| dispatchDisplayModeChangeEvent(displayId, activeMode); |
| } |
| } |
| |
| void SurfaceFlinger::dropModeRequest(PhysicalDisplayId displayId) { |
| mDisplayModeController.clearDesiredMode(displayId); |
| if (displayId == mActiveDisplayId) { |
| // TODO(b/255635711): Check for pending mode changes on other displays. |
| mScheduler->setModeChangePending(false); |
| } |
| } |
| |
| void SurfaceFlinger::applyActiveMode(PhysicalDisplayId displayId) { |
| const auto activeModeOpt = mDisplayModeController.getDesiredMode(displayId); |
| auto activeModePtr = activeModeOpt->mode.modePtr; |
| const auto renderFps = activeModeOpt->mode.fps; |
| |
| dropModeRequest(displayId); |
| |
| constexpr bool kAllowToEnable = true; |
| mScheduler->resyncToHardwareVsync(displayId, kAllowToEnable, std::move(activeModePtr).take()); |
| mScheduler->setRenderRate(displayId, renderFps, /*applyImmediately*/ true); |
| |
| if (displayId == mActiveDisplayId) { |
| mScheduler->updatePhaseConfiguration(renderFps); |
| } |
| } |
| |
| void SurfaceFlinger::initiateDisplayModeChanges() { |
| ATRACE_CALL(); |
| |
| std::optional<PhysicalDisplayId> displayToUpdateImmediately; |
| |
| for (const auto& [displayId, physical] : mPhysicalDisplays) { |
| auto desiredModeOpt = mDisplayModeController.getDesiredMode(displayId); |
| if (!desiredModeOpt) { |
| continue; |
| } |
| |
| const auto desiredModeId = desiredModeOpt->mode.modePtr->getId(); |
| const auto displayModePtrOpt = physical.snapshot().displayModes().get(desiredModeId); |
| |
| if (!displayModePtrOpt) { |
| ALOGW("Desired display mode is no longer supported. Mode ID = %d", |
| ftl::to_underlying(desiredModeId)); |
| continue; |
| } |
| |
| ALOGV("%s changing active mode to %d(%s) for display %s", __func__, |
| ftl::to_underlying(desiredModeId), |
| to_string(displayModePtrOpt->get()->getVsyncRate()).c_str(), |
| to_string(displayId).c_str()); |
| |
| if ((!FlagManager::getInstance().connected_display() || !desiredModeOpt->force) && |
| mDisplayModeController.getActiveMode(displayId) == desiredModeOpt->mode) { |
| applyActiveMode(displayId); |
| continue; |
| } |
| |
| const auto selectorPtr = mDisplayModeController.selectorPtrFor(displayId); |
| |
| // Desired active mode was set, it is different than the mode currently in use, however |
| // allowed modes might have changed by the time we process the refresh. |
| // Make sure the desired mode is still allowed |
| if (!selectorPtr->isModeAllowed(desiredModeOpt->mode)) { |
| dropModeRequest(displayId); |
| continue; |
| } |
| |
| // TODO(b/142753666) use constrains |
| hal::VsyncPeriodChangeConstraints constraints; |
| constraints.desiredTimeNanos = systemTime(); |
| constraints.seamlessRequired = false; |
| hal::VsyncPeriodChangeTimeline outTimeline; |
| |
| if (!mDisplayModeController.initiateModeChange(displayId, std::move(*desiredModeOpt), |
| constraints, outTimeline)) { |
| continue; |
| } |
| |
| selectorPtr->onModeChangeInitiated(); |
| mScheduler->onNewVsyncPeriodChangeTimeline(outTimeline); |
| |
| if (outTimeline.refreshRequired) { |
| scheduleComposite(FrameHint::kNone); |
| } else { |
| // TODO(b/255635711): Remove `displayToUpdateImmediately` to `finalizeDisplayModeChange` |
| // for all displays. This was only needed when the loop iterated over `mDisplays` rather |
| // than `mPhysicalDisplays`. |
| displayToUpdateImmediately = displayId; |
| } |
| } |
| |
| if (displayToUpdateImmediately) { |
| const auto displayId = *displayToUpdateImmediately; |
| finalizeDisplayModeChange(displayId); |
| |
| const auto desiredModeOpt = mDisplayModeController.getDesiredMode(displayId); |
| if (desiredModeOpt && |
| mDisplayModeController.getActiveMode(displayId) == desiredModeOpt->mode) { |
| applyActiveMode(displayId); |
| } |
| } |
| } |
| |
| void SurfaceFlinger::disableExpensiveRendering() { |
| const char* const whence = __func__; |
| auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) { |
| ATRACE_NAME(whence); |
| if (mPowerAdvisor->isUsingExpensiveRendering()) { |
| for (const auto& [_, display] : mDisplays) { |
| constexpr bool kDisable = false; |
| mPowerAdvisor->setExpensiveRenderingExpected(display->getId(), kDisable); |
| } |
| } |
| }); |
| |
| future.wait(); |
| } |
| |
| status_t SurfaceFlinger::getDisplayNativePrimaries(const sp<IBinder>& displayToken, |
| ui::DisplayPrimaries& primaries) { |
| if (!displayToken) { |
| return BAD_VALUE; |
| } |
| |
| Mutex::Autolock lock(mStateLock); |
| |
| const auto display = ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken)) |
| .transform(&ftl::to_mapped_ref<PhysicalDisplays>); |
| if (!display) { |
| return NAME_NOT_FOUND; |
| } |
| |
| if (!display.transform(&PhysicalDisplay::isInternal).value()) { |
| return INVALID_OPERATION; |
| } |
| |
| // TODO(b/229846990): For now, assume that all internal displays have the same primaries. |
| primaries = mInternalDisplayPrimaries; |
| return NO_ERROR; |
| } |
| |
| status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& displayToken, ui::ColorMode mode) { |
| if (!displayToken) { |
| return BAD_VALUE; |
| } |
| |
| const char* const whence = __func__; |
| auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) -> status_t { |
| const auto displayOpt = |
| ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken)) |
| .transform(&ftl::to_mapped_ref<PhysicalDisplays>) |
| .and_then(getDisplayDeviceAndSnapshot()); |
| |
| if (!displayOpt) { |
| ALOGE("%s: Invalid physical display token %p", whence, displayToken.get()); |
| return NAME_NOT_FOUND; |
| } |
| |
| const auto& [display, snapshotRef] = *displayOpt; |
| const auto& snapshot = snapshotRef.get(); |
| |
| const auto modes = snapshot.filterColorModes(mSupportsWideColor); |
| const bool exists = std::find(modes.begin(), modes.end(), mode) != modes.end(); |
| |
| if (mode < ui::ColorMode::NATIVE || !exists) { |
| ALOGE("%s: Invalid color mode %s (%d) for display %s", whence, |
| decodeColorMode(mode).c_str(), mode, to_string(snapshot.displayId()).c_str()); |
| return BAD_VALUE; |
| } |
| |
| display->getCompositionDisplay()->setColorProfile( |
| {mode, Dataspace::UNKNOWN, RenderIntent::COLORIMETRIC}); |
| |
| return NO_ERROR; |
| }); |
| |
| // TODO(b/195698395): Propagate error. |
| future.wait(); |
| return NO_ERROR; |
| } |
| |
| status_t SurfaceFlinger::getBootDisplayModeSupport(bool* outSupport) const { |
| auto future = mScheduler->schedule( |
| [this] { return getHwComposer().hasCapability(Capability::BOOT_DISPLAY_CONFIG); }); |
| |
| *outSupport = future.get(); |
| return NO_ERROR; |
| } |
| |
| status_t SurfaceFlinger::getOverlaySupport(gui::OverlayProperties* outProperties) const { |
| const auto& aidlProperties = getHwComposer().getOverlaySupport(); |
| // convert aidl OverlayProperties to gui::OverlayProperties |
| outProperties->combinations.reserve(aidlProperties.combinations.size()); |
| for (const auto& combination : aidlProperties.combinations) { |
| std::vector<int32_t> pixelFormats; |
| pixelFormats.reserve(combination.pixelFormats.size()); |
| std::transform(combination.pixelFormats.cbegin(), combination.pixelFormats.cend(), |
| std::back_inserter(pixelFormats), |
| [](const auto& val) { return static_cast<int32_t>(val); }); |
| std::vector<int32_t> standards; |
| standards.reserve(combination.standards.size()); |
| std::transform(combination.standards.cbegin(), combination.standards.cend(), |
| std::back_inserter(standards), |
| [](const auto& val) { return static_cast<int32_t>(val); }); |
| std::vector<int32_t> transfers; |
| transfers.reserve(combination.transfers.size()); |
| std::transform(combination.transfers.cbegin(), combination.transfers.cend(), |
| std::back_inserter(transfers), |
| [](const auto& val) { return static_cast<int32_t>(val); }); |
| std::vector<int32_t> ranges; |
| ranges.reserve(combination.ranges.size()); |
| std::transform(combination.ranges.cbegin(), combination.ranges.cend(), |
| std::back_inserter(ranges), |
| [](const auto& val) { return static_cast<int32_t>(val); }); |
| gui::OverlayProperties::SupportedBufferCombinations outCombination; |
| outCombination.pixelFormats = std::move(pixelFormats); |
| outCombination.standards = std::move(standards); |
| outCombination.transfers = std::move(transfers); |
| outCombination.ranges = std::move(ranges); |
| outProperties->combinations.emplace_back(outCombination); |
| } |
| outProperties->supportMixedColorSpaces = aidlProperties.supportMixedColorSpaces; |
| return NO_ERROR; |
| } |
| |
| status_t SurfaceFlinger::setBootDisplayMode(const sp<display::DisplayToken>& displayToken, |
| DisplayModeId modeId) { |
| const char* const whence = __func__; |
| auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) -> status_t { |
| const auto snapshotOpt = |
| ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken)) |
| .transform(&ftl::to_mapped_ref<PhysicalDisplays>) |
| .transform(&PhysicalDisplay::snapshotRef); |
| |
| if (!snapshotOpt) { |
| ALOGE("%s: Invalid physical display token %p", whence, displayToken.get()); |
| return NAME_NOT_FOUND; |
| } |
| |
| const auto& snapshot = snapshotOpt->get(); |
| const auto hwcIdOpt = snapshot.displayModes().get(modeId).transform( |
| [](const DisplayModePtr& mode) { return mode->getHwcId(); }); |
| |
| if (!hwcIdOpt) { |
| ALOGE("%s: Invalid mode %d for display %s", whence, ftl::to_underlying(modeId), |
| to_string(snapshot.displayId()).c_str()); |
| return BAD_VALUE; |
| } |
| |
| return getHwComposer().setBootDisplayMode(snapshot.displayId(), *hwcIdOpt); |
| }); |
| return future.get(); |
| } |
| |
| status_t SurfaceFlinger::clearBootDisplayMode(const sp<IBinder>& displayToken) { |
| const char* const whence = __func__; |
| auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) -> status_t { |
| if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) { |
| return getHwComposer().clearBootDisplayMode(*displayId); |
| } else { |
| ALOGE("%s: Invalid display token %p", whence, displayToken.get()); |
| return BAD_VALUE; |
| } |
| }); |
| return future.get(); |
| } |
| |
| status_t SurfaceFlinger::getHdrConversionCapabilities( |
| std::vector<gui::HdrConversionCapability>* hdrConversionCapabilities) const { |
| bool hdrOutputConversionSupport; |
| getHdrOutputConversionSupport(&hdrOutputConversionSupport); |
| if (hdrOutputConversionSupport == false) { |
| ALOGE("hdrOutputConversion is not supported by this device."); |
| return INVALID_OPERATION; |
| } |
| const auto aidlConversionCapability = getHwComposer().getHdrConversionCapabilities(); |
| for (auto capability : aidlConversionCapability) { |
| gui::HdrConversionCapability tempCapability; |
| tempCapability.sourceType = static_cast<int>(capability.sourceType); |
| tempCapability.outputType = static_cast<int>(capability.outputType); |
| tempCapability.addsLatency = capability.addsLatency; |
| hdrConversionCapabilities->push_back(tempCapability); |
| } |
| return NO_ERROR; |
| } |
| |
| status_t SurfaceFlinger::setHdrConversionStrategy( |
| const gui::HdrConversionStrategy& hdrConversionStrategy, |
| int32_t* outPreferredHdrOutputType) { |
| bool hdrOutputConversionSupport; |
| getHdrOutputConversionSupport(&hdrOutputConversionSupport); |
| if (hdrOutputConversionSupport == false) { |
| ALOGE("hdrOutputConversion is not supported by this device."); |
| return INVALID_OPERATION; |
| } |
| auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) mutable -> status_t { |
| using AidlHdrConversionStrategy = |
| aidl::android::hardware::graphics::common::HdrConversionStrategy; |
| using GuiHdrConversionStrategyTag = gui::HdrConversionStrategy::Tag; |
| AidlHdrConversionStrategy aidlConversionStrategy; |
| status_t status; |
| aidl::android::hardware::graphics::common::Hdr aidlPreferredHdrOutputType; |
| switch (hdrConversionStrategy.getTag()) { |
| case GuiHdrConversionStrategyTag::passthrough: { |
| aidlConversionStrategy.set<AidlHdrConversionStrategy::Tag::passthrough>( |
| hdrConversionStrategy.get<GuiHdrConversionStrategyTag::passthrough>()); |
| status = getHwComposer().setHdrConversionStrategy(aidlConversionStrategy, |
| &aidlPreferredHdrOutputType); |
| *outPreferredHdrOutputType = static_cast<int32_t>(aidlPreferredHdrOutputType); |
| return status; |
| } |
| case GuiHdrConversionStrategyTag::autoAllowedHdrTypes: { |
| auto autoHdrTypes = |
| hdrConversionStrategy |
| .get<GuiHdrConversionStrategyTag::autoAllowedHdrTypes>(); |
| std::vector<aidl::android::hardware::graphics::common::Hdr> aidlAutoHdrTypes; |
| for (auto type : autoHdrTypes) { |
| aidlAutoHdrTypes.push_back( |
| static_cast<aidl::android::hardware::graphics::common::Hdr>(type)); |
| } |
| aidlConversionStrategy.set<AidlHdrConversionStrategy::Tag::autoAllowedHdrTypes>( |
| aidlAutoHdrTypes); |
| status = getHwComposer().setHdrConversionStrategy(aidlConversionStrategy, |
| &aidlPreferredHdrOutputType); |
| *outPreferredHdrOutputType = static_cast<int32_t>(aidlPreferredHdrOutputType); |
| return status; |
| } |
| case GuiHdrConversionStrategyTag::forceHdrConversion: { |
| auto forceHdrConversion = |
| hdrConversionStrategy |
| .get<GuiHdrConversionStrategyTag::forceHdrConversion>(); |
| aidlConversionStrategy.set<AidlHdrConversionStrategy::Tag::forceHdrConversion>( |
| static_cast<aidl::android::hardware::graphics::common::Hdr>( |
| forceHdrConversion)); |
| status = getHwComposer().setHdrConversionStrategy(aidlConversionStrategy, |
| &aidlPreferredHdrOutputType); |
| *outPreferredHdrOutputType = static_cast<int32_t>(aidlPreferredHdrOutputType); |
| return status; |
| } |
| } |
| }); |
| return future.get(); |
| } |
| |
| status_t SurfaceFlinger::getHdrOutputConversionSupport(bool* outSupport) const { |
| auto future = mScheduler->schedule([this] { |
| return getHwComposer().hasCapability(Capability::HDR_OUTPUT_CONVERSION_CONFIG); |
| }); |
| |
| *outSupport = future.get(); |
| return NO_ERROR; |
| } |
| |
| void SurfaceFlinger::setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) { |
| const char* const whence = __func__; |
| static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) { |
| if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) { |
| getHwComposer().setAutoLowLatencyMode(*displayId, on); |
| } else { |
| ALOGE("%s: Invalid display token %p", whence, displayToken.get()); |
| } |
| })); |
| } |
| |
| void SurfaceFlinger::setGameContentType(const sp<IBinder>& displayToken, bool on) { |
| const char* const whence = __func__; |
| static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) { |
| if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) { |
| const auto type = on ? hal::ContentType::GAME : hal::ContentType::NONE; |
| getHwComposer().setContentType(*displayId, type); |
| } else { |
| ALOGE("%s: Invalid display token %p", whence, displayToken.get()); |
| } |
| })); |
| } |
| |
| status_t SurfaceFlinger::overrideHdrTypes(const sp<IBinder>& displayToken, |
| const std::vector<ui::Hdr>& hdrTypes) { |
| Mutex::Autolock lock(mStateLock); |
| |
| auto display = getDisplayDeviceLocked(displayToken); |
| if (!display) { |
| ALOGE("%s: Invalid display token %p", __func__, displayToken.get()); |
| return NAME_NOT_FOUND; |
| } |
| |
| display->overrideHdrTypes(hdrTypes); |
| mScheduler->dispatchHotplug(display->getPhysicalId(), scheduler::Scheduler::Hotplug::Connected); |
| return NO_ERROR; |
| } |
| |
| status_t SurfaceFlinger::onPullAtom(const int32_t atomId, std::vector<uint8_t>* pulledData, |
| bool* success) { |
| *success = mTimeStats->onPullAtom(atomId, pulledData); |
| return NO_ERROR; |
| } |
| |
| status_t SurfaceFlinger::getDisplayedContentSamplingAttributes(const sp<IBinder>& displayToken, |
| ui::PixelFormat* outFormat, |
| ui::Dataspace* outDataspace, |
| uint8_t* outComponentMask) const { |
| if (!outFormat || !outDataspace || !outComponentMask) { |
| return BAD_VALUE; |
| } |
| |
| Mutex::Autolock lock(mStateLock); |
| |
| const auto displayId = getPhysicalDisplayIdLocked(displayToken); |
| if (!displayId) { |
| return NAME_NOT_FOUND; |
| } |
| |
| return getHwComposer().getDisplayedContentSamplingAttributes(*displayId, outFormat, |
| outDataspace, outComponentMask); |
| } |
| |
| status_t SurfaceFlinger::setDisplayContentSamplingEnabled(const sp<IBinder>& displayToken, |
| bool enable, uint8_t componentMask, |
| uint64_t maxFrames) { |
| const char* const whence = __func__; |
| auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) -> status_t { |
| if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) { |
| return getHwComposer().setDisplayContentSamplingEnabled(*displayId, enable, |
| componentMask, maxFrames); |
| } else { |
| ALOGE("%s: Invalid display token %p", whence, displayToken.get()); |
| return NAME_NOT_FOUND; |
| } |
| }); |
| |
| return future.get(); |
| } |
| |
| status_t SurfaceFlinger::getDisplayedContentSample(const sp<IBinder>& displayToken, |
| uint64_t maxFrames, uint64_t timestamp, |
| DisplayedFrameStats* outStats) const { |
| Mutex::Autolock lock(mStateLock); |
| |
| const auto displayId = getPhysicalDisplayIdLocked(displayToken); |
| if (!displayId) { |
| return NAME_NOT_FOUND; |
| } |
| |
| return getHwComposer().getDisplayedContentSample(*displayId, maxFrames, timestamp, outStats); |
| } |
| |
| status_t SurfaceFlinger::getProtectedContentSupport(bool* outSupported) const { |
| if (!outSupported) { |
| return BAD_VALUE; |
| } |
| *outSupported = getRenderEngine().supportsProtectedContent(); |
| return NO_ERROR; |
| } |
| |
| status_t SurfaceFlinger::isWideColorDisplay(const sp<IBinder>& displayToken, |
| bool* outIsWideColorDisplay) const { |
| if (!displayToken || !outIsWideColorDisplay) { |
| return BAD_VALUE; |
| } |
| |
| Mutex::Autolock lock(mStateLock); |
| const auto display = getDisplayDeviceLocked(displayToken); |
| if (!display) { |
| return NAME_NOT_FOUND; |
| } |
| |
| *outIsWideColorDisplay = |
| display->isPrimary() ? mSupportsWideColor : display->hasWideColorGamut(); |
| return NO_ERROR; |
| } |
| |
| status_t SurfaceFlinger::getCompositionPreference( |
| Dataspace* outDataspace, ui::PixelFormat* outPixelFormat, |
| Dataspace* outWideColorGamutDataspace, |
| ui::PixelFormat* outWideColorGamutPixelFormat) const { |
| *outDataspace = mDefaultCompositionDataspace; |
| *outPixelFormat = defaultCompositionPixelFormat; |
| *outWideColorGamutDataspace = mWideColorGamutCompositionDataspace; |
| *outWideColorGamutPixelFormat = wideColorGamutCompositionPixelFormat; |
| return NO_ERROR; |
| } |
| |
| status_t SurfaceFlinger::addRegionSamplingListener(const Rect& samplingArea, |
| const sp<IBinder>& stopLayerHandle, |
| const sp<IRegionSamplingListener>& listener) { |
| if (!listener || samplingArea == Rect::INVALID_RECT || samplingArea.isEmpty()) { |
| return BAD_VALUE; |
| } |
| |
| // LayerHandle::getLayer promotes the layer object in a binder thread but we will not destroy |
| // the layer here since the caller has a strong ref to the layer's handle. |
| const sp<Layer> stopLayer = LayerHandle::getLayer(stopLayerHandle); |
| mRegionSamplingThread->addListener(samplingArea, |
| stopLayer ? stopLayer->getSequence() : UNASSIGNED_LAYER_ID, |
| listener); |
| return NO_ERROR; |
| } |
| |
| status_t SurfaceFlinger::removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) { |
| if (!listener) { |
| return BAD_VALUE; |
| } |
| mRegionSamplingThread->removeListener(listener); |
| return NO_ERROR; |
| } |
| |
| status_t SurfaceFlinger::addFpsListener(int32_t taskId, const sp<gui::IFpsListener>& listener) { |
| if (!listener) { |
| return BAD_VALUE; |
| } |
| |
| mFpsReporter->addListener(listener, taskId); |
| return NO_ERROR; |
| } |
| |
| status_t SurfaceFlinger::removeFpsListener(const sp<gui::IFpsListener>& listener) { |
| if (!listener) { |
| return BAD_VALUE; |
| } |
| mFpsReporter->removeListener(listener); |
| return NO_ERROR; |
| } |
| |
| status_t SurfaceFlinger::addTunnelModeEnabledListener( |
| const sp<gui::ITunnelModeEnabledListener>& listener) { |
| if (!listener) { |
| return BAD_VALUE; |
| } |
| |
| mTunnelModeEnabledReporter->addListener(listener); |
| return NO_ERROR; |
| } |
| |
| status_t SurfaceFlinger::removeTunnelModeEnabledListener( |
| const sp<gui::ITunnelModeEnabledListener>& listener) { |
| if (!listener) { |
| return BAD_VALUE; |
| } |
| |
| mTunnelModeEnabledReporter->removeListener(listener); |
| return NO_ERROR; |
| } |
| |
| status_t SurfaceFlinger::getDisplayBrightnessSupport(const sp<IBinder>& displayToken, |
| bool* outSupport) const { |
| if (!displayToken || !outSupport) { |
| return BAD_VALUE; |
| } |
| |
| Mutex::Autolock lock(mStateLock); |
| |
| const auto displayId = getPhysicalDisplayIdLocked(displayToken); |
| if (!displayId) { |
| return NAME_NOT_FOUND; |
| } |
| *outSupport = getHwComposer().hasDisplayCapability(*displayId, DisplayCapability::BRIGHTNESS); |
| return NO_ERROR; |
| } |
| |
| status_t SurfaceFlinger::setDisplayBrightness(const sp<IBinder>& displayToken, |
| const gui::DisplayBrightness& brightness) { |
| if (!displayToken) { |
| return BAD_VALUE; |
| } |
| |
| const char* const whence = __func__; |
| return ftl::Future(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) { |
| // TODO(b/241285876): Validate that the display is physical instead of failing later. |
| if (const auto display = getDisplayDeviceLocked(displayToken)) { |
| const bool supportsDisplayBrightnessCommand = |
| getHwComposer().getComposer()->isSupported( |
| Hwc2::Composer::OptionalFeature::DisplayBrightnessCommand); |
| // If we support applying display brightness as a command, then we also support |
| // dimming SDR layers. |
| if (supportsDisplayBrightnessCommand) { |
| auto compositionDisplay = display->getCompositionDisplay(); |
| float currentDimmingRatio = |
| compositionDisplay->editState().sdrWhitePointNits / |
| compositionDisplay->editState().displayBrightnessNits; |
| static constexpr float kDimmingThreshold = 0.02f; |
| if (brightness.sdrWhitePointNits == 0.f || |
| abs(brightness.sdrWhitePointNits - brightness.displayBrightnessNits) / |
| brightness.sdrWhitePointNits >= |
| kDimmingThreshold) { |
| // to optimize, skip brightness setter if the brightness difference ratio |
| // is lower than threshold |
| compositionDisplay |
| ->setDisplayBrightness(brightness.sdrWhitePointNits, |
| brightness.displayBrightnessNits); |
| } else { |
| compositionDisplay->setDisplayBrightness(brightness.sdrWhitePointNits, |
| brightness.sdrWhitePointNits); |
| } |
| |
| FTL_FAKE_GUARD(kMainThreadContext, |
| display->stageBrightness(brightness.displayBrightness)); |
| float currentHdrSdrRatio = |
| compositionDisplay->editState().displayBrightnessNits / |
| compositionDisplay->editState().sdrWhitePointNits; |
| FTL_FAKE_GUARD(kMainThreadContext, |
| display->updateHdrSdrRatioOverlayRatio(currentHdrSdrRatio)); |
| |
| if (brightness.sdrWhitePointNits / brightness.displayBrightnessNits != |
| currentDimmingRatio) { |
| scheduleComposite(FrameHint::kNone); |
| } else { |
| scheduleCommit(FrameHint::kNone); |
| } |
| return ftl::yield<status_t>(OK); |
| } else { |
| return getHwComposer() |
| .setDisplayBrightness(display->getPhysicalId(), |
| brightness.displayBrightness, |
| brightness.displayBrightnessNits, |
| Hwc2::Composer::DisplayBrightnessOptions{ |
| .applyImmediately = true}); |
| } |
| } else { |
| ALOGE("%s: Invalid display token %p", whence, displayToken.get()); |
| return ftl::yield<status_t>(NAME_NOT_FOUND); |
| } |
| })) |
| .then([](ftl::Future<status_t> task) { return task; }) |
| .get(); |
| } |
| |
| status_t SurfaceFlinger::addHdrLayerInfoListener(const sp<IBinder>& displayToken, |
| const sp<gui::IHdrLayerInfoListener>& listener) { |
| if (!displayToken) { |
| return BAD_VALUE; |
| } |
| |
| Mutex::Autolock lock(mStateLock); |
| |
| const auto display = getDisplayDeviceLocked(displayToken); |
| if (!display) { |
| return NAME_NOT_FOUND; |
| } |
| const auto displayId = display->getId(); |
| sp<HdrLayerInfoReporter>& hdrInfoReporter = mHdrLayerInfoListeners[displayId]; |
| if (!hdrInfoReporter) { |
| hdrInfoReporter = sp<HdrLayerInfoReporter>::make(); |
| } |
| hdrInfoReporter->addListener(listener); |
| |
| |
| mAddingHDRLayerInfoListener = true; |
| return OK; |
| } |
| |
| status_t SurfaceFlinger::removeHdrLayerInfoListener( |
| const sp<IBinder>& displayToken, const sp<gui::IHdrLayerInfoListener>& listener) { |
| if (!displayToken) { |
| return BAD_VALUE; |
| } |
| |
| Mutex::Autolock lock(mStateLock); |
| |
| const auto display = getDisplayDeviceLocked(displayToken); |
| if (!display) { |
| return NAME_NOT_FOUND; |
| } |
| const auto displayId = display->getId(); |
| sp<HdrLayerInfoReporter>& hdrInfoReporter = mHdrLayerInfoListeners[displayId]; |
| if (hdrInfoReporter) { |
| hdrInfoReporter->removeListener(listener); |
| } |
| return OK; |
| } |
| |
| status_t SurfaceFlinger::notifyPowerBoost(int32_t boostId) { |
| using aidl::android::hardware::power::Boost; |
| Boost powerBoost = static_cast<Boost>(boostId); |
| |
| if (powerBoost == Boost::INTERACTION) { |
| mScheduler->onTouchHint(); |
| } |
| |
| return NO_ERROR; |
| } |
| |
| status_t SurfaceFlinger::getDisplayDecorationSupport( |
| const sp<IBinder>& displayToken, |
| std::optional<DisplayDecorationSupport>* outSupport) const { |
| if (!displayToken || !outSupport) { |
| return BAD_VALUE; |
| } |
| |
| Mutex::Autolock lock(mStateLock); |
| |
| const auto displayId = getPhysicalDisplayIdLocked(displayToken); |
| if (!displayId) { |
| return NAME_NOT_FOUND; |
| } |
| getHwComposer().getDisplayDecorationSupport(*displayId, outSupport); |
| return NO_ERROR; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| |
| sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection( |
| gui::ISurfaceComposer::VsyncSource vsyncSource, EventRegistrationFlags eventRegistration, |
| const sp<IBinder>& layerHandle) { |
| const auto cycle = [&] { |
| if (FlagManager::getInstance().deprecate_vsync_sf()) { |
| ALOGW_IF(vsyncSource == gui::ISurfaceComposer::VsyncSource::eVsyncSourceSurfaceFlinger, |
| "requested unsupported config eVsyncSourceSurfaceFlinger"); |
| return scheduler::Cycle::Render; |
| } |
| |
| return vsyncSource == gui::ISurfaceComposer::VsyncSource::eVsyncSourceSurfaceFlinger |
| ? scheduler::Cycle::LastComposite |
| : scheduler::Cycle::Render; |
| }(); |
| return mScheduler->createDisplayEventConnection(cycle, eventRegistration, layerHandle); |
| } |
| |
| void SurfaceFlinger::scheduleCommit(FrameHint hint) { |
| if (hint == FrameHint::kActive) { |
| mScheduler->resetIdleTimer(); |
| } |
| mPowerAdvisor->notifyDisplayUpdateImminentAndCpuReset(); |
| mScheduler->scheduleFrame(); |
| } |
| |
| void SurfaceFlinger::scheduleComposite(FrameHint hint) { |
| mMustComposite = true; |
| scheduleCommit(hint); |
| } |
| |
| void SurfaceFlinger::scheduleRepaint() { |
| mGeometryDirty = true; |
| scheduleComposite(FrameHint::kActive); |
| } |
| |
| void SurfaceFlinger::scheduleSample() { |
| static_cast<void>(mScheduler->schedule([this] { sample(); })); |
| } |
| |
| nsecs_t SurfaceFlinger::getVsyncPeriodFromHWC() const { |
| if (const auto display = getDefaultDisplayDeviceLocked()) { |
| return display->getVsyncPeriodFromHWC(); |
| } |
| |
| return 0; |
| } |
| |
| void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp, |
| std::optional<hal::VsyncPeriodNanos> vsyncPeriod) { |
| if (FlagManager::getInstance().connected_display() && timestamp < 0 && |
| vsyncPeriod.has_value()) { |
| // use ~0 instead of -1 as AidlComposerHal.cpp passes the param as unsigned int32 |
| if (mIsHotplugErrViaNegVsync && vsyncPeriod.value() == ~0) { |
| const auto errorCode = static_cast<int32_t>(-timestamp); |
| ALOGD("%s: Hotplug error %d for display %" PRIu64, __func__, errorCode, hwcDisplayId); |
| mScheduler->dispatchHotplugError(errorCode); |
| return; |
| } |
| |
| if (mIsHdcpViaNegVsync && vsyncPeriod.value() == ~1) { |
| const int32_t value = static_cast<int32_t>(-timestamp); |
| // one byte is good enough to encode android.hardware.drm.HdcpLevel |
| const int32_t maxLevel = (value >> 8) & 0xFF; |
| const int32_t connectedLevel = value & 0xFF; |
| ALOGD("%s: HDCP levels changed (connected=%d, max=%d) for display %" PRIu64, __func__, |
| connectedLevel, maxLevel, hwcDisplayId); |
| updateHdcpLevels(hwcDisplayId, connectedLevel, maxLevel); |
| return; |
| } |
| } |
| |
| ATRACE_NAME(vsyncPeriod |
| ? ftl::Concat(__func__, ' ', hwcDisplayId, ' ', *vsyncPeriod, "ns").c_str() |
| : ftl::Concat(__func__, ' ', hwcDisplayId).c_str()); |
| |
| Mutex::Autolock lock(mStateLock); |
| if (const auto displayIdOpt = getHwComposer().onVsync(hwcDisplayId, timestamp)) { |
| if (mScheduler->addResyncSample(*displayIdOpt, timestamp, vsyncPeriod)) { |
| // period flushed |
| mScheduler->modulateVsync(displayIdOpt, &VsyncModulator::onRefreshRateChangeCompleted); |
| } |
| } |
| } |
| |
| void SurfaceFlinger::onComposerHalHotplugEvent(hal::HWDisplayId hwcDisplayId, |
| DisplayHotplugEvent event) { |
| if (event == DisplayHotplugEvent::CONNECTED || event == DisplayHotplugEvent::DISCONNECTED) { |
| hal::Connection connection = (event == DisplayHotplugEvent::CONNECTED) |
| ? hal::Connection::CONNECTED |
| : hal::Connection::DISCONNECTED; |
| { |
| std::lock_guard<std::mutex> lock(mHotplugMutex); |
| mPendingHotplugEvents.push_back(HotplugEvent{hwcDisplayId, connection}); |
| } |
| |
| if (mScheduler) { |
| mScheduler->scheduleConfigure(); |
| } |
| |
| return; |
| } |
| |
| if (FlagManager::getInstance().hotplug2()) { |
| // TODO(b/311403559): use enum type instead of int |
| const auto errorCode = static_cast<int32_t>(event); |
| ALOGD("%s: Hotplug error %d for display %" PRIu64, __func__, errorCode, hwcDisplayId); |
| mScheduler->dispatchHotplugError(errorCode); |
| } |
| } |
| |
| void SurfaceFlinger::onComposerHalVsyncPeriodTimingChanged( |
| hal::HWDisplayId, const hal::VsyncPeriodChangeTimeline& timeline) { |
| Mutex::Autolock lock(mStateLock); |
| mScheduler->onNewVsyncPeriodChangeTimeline(timeline); |
| |
| if (timeline.refreshRequired) { |
| scheduleComposite(FrameHint::kNone); |
| } |
| } |
| |
| void SurfaceFlinger::onComposerHalSeamlessPossible(hal::HWDisplayId) { |
| // TODO(b/142753666): use constraints when calling to setActiveModeWithConstraints and |
| // use this callback to know when to retry in case of SEAMLESS_NOT_POSSIBLE. |
| } |
| |
| void SurfaceFlinger::onComposerHalRefresh(hal::HWDisplayId) { |
| Mutex::Autolock lock(mStateLock); |
| scheduleComposite(FrameHint::kNone); |
| } |
| |
| void SurfaceFlinger::onComposerHalVsyncIdle(hal::HWDisplayId) { |
| ATRACE_CALL(); |
| mScheduler->forceNextResync(); |
| } |
| |
| void SurfaceFlinger::onRefreshRateChangedDebug(const RefreshRateChangedDebugData& data) { |
| ATRACE_CALL(); |
| const char* const whence = __func__; |
| static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD( |
| kMainThreadContext) { |
| if (const auto displayIdOpt = getHwComposer().toPhysicalDisplayId(data.display)) { |
| if (const auto display = getDisplayDeviceLocked(*displayIdOpt)) { |
| const Fps refreshRate = Fps::fromPeriodNsecs( |
| getHwComposer().getComposer()->isVrrSupported() ? data.refreshPeriodNanos |
| : data.vsyncPeriodNanos); |
| ATRACE_FORMAT("%s refresh rate = %d", whence, refreshRate.getIntValue()); |
| |
| const auto renderRate = mDisplayModeController.getActiveMode(*displayIdOpt).fps; |
| constexpr bool kSetByHwc = true; |
| display->updateRefreshRateOverlayRate(refreshRate, renderRate, kSetByHwc); |
| } |
| } |
| })); |
| } |
| |
| void SurfaceFlinger::configure() { |
| Mutex::Autolock lock(mStateLock); |
| if (configureLocked()) { |
| setTransactionFlags(eDisplayTransactionNeeded); |
| } |
| } |
| |
| bool SurfaceFlinger::updateLayerSnapshotsLegacy(VsyncId vsyncId, nsecs_t frameTimeNs, |
| bool flushTransactions, |
| bool& outTransactionsAreEmpty) { |
| ATRACE_CALL(); |
| frontend::Update update; |
| if (flushTransactions) { |
| update = flushLifecycleUpdates(); |
| if (mTransactionTracing) { |
| mTransactionTracing->addCommittedTransactions(ftl::to_underlying(vsyncId), frameTimeNs, |
| update, mFrontEndDisplayInfos, |
| mFrontEndDisplayInfosChanged); |
| } |
| } |
| |
| bool needsTraversal = false; |
| if (flushTransactions) { |
| needsTraversal |= commitMirrorDisplays(vsyncId); |
| needsTraversal |= commitCreatedLayers(vsyncId, update.layerCreatedStates); |
| needsTraversal |= applyTransactions(update.transactions); |
| } |
| outTransactionsAreEmpty = !needsTraversal; |
| const bool shouldCommit = (getTransactionFlags() & ~eTransactionFlushNeeded) || needsTraversal; |
| if (shouldCommit) { |
| commitTransactionsLegacy(); |
| } |
| |
| bool mustComposite = latchBuffers() || shouldCommit; |
| updateLayerGeometry(); |
| return mustComposite; |
| } |
| |
| void SurfaceFlinger::updateLayerHistory(nsecs_t now) { |
| for (const auto& snapshot : mLayerSnapshotBuilder.getSnapshots()) { |
| using Changes = frontend::RequestedLayerState::Changes; |
| if (snapshot->path.isClone()) { |
| continue; |
| } |
| |
| const bool updateSmallDirty = FlagManager::getInstance().enable_small_area_detection() && |
| ((snapshot->clientChanges & layer_state_t::eSurfaceDamageRegionChanged) || |
| snapshot->changes.any(Changes::Geometry)); |
| |
| const bool hasChanges = |
| snapshot->changes.any(Changes::FrameRate | Changes::Buffer | Changes::Animation | |
| Changes::Geometry | Changes::Visibility) || |
| (snapshot->clientChanges & layer_state_t::eDefaultFrameRateCompatibilityChanged) != |
| 0; |
| |
| if (!updateSmallDirty && !hasChanges) { |
| continue; |
| } |
| |
| auto it = mLegacyLayers.find(snapshot->sequence); |
| LLOG_ALWAYS_FATAL_WITH_TRACE_IF(it == mLegacyLayers.end(), |
| "Couldn't find layer object for %s", |
| snapshot->getDebugString().c_str()); |
| |
| if (updateSmallDirty) { |
| // Update small dirty flag while surface damage region or geometry changed |
| it->second->setIsSmallDirty(snapshot.get()); |
| } |
| |
| if (!hasChanges) { |
| continue; |
| } |
| |
| const auto layerProps = scheduler::LayerProps{ |
| .visible = snapshot->isVisible, |
| .bounds = snapshot->geomLayerBounds, |
| .transform = snapshot->geomLayerTransform, |
| .setFrameRateVote = snapshot->frameRate, |
| .frameRateSelectionPriority = snapshot->frameRateSelectionPriority, |
| .isSmallDirty = snapshot->isSmallDirty, |
| .isFrontBuffered = snapshot->isFrontBuffered(), |
| }; |
| |
| if (snapshot->changes.any(Changes::Geometry | Changes::Visibility)) { |
| mScheduler->setLayerProperties(snapshot->sequence, layerProps); |
| } |
| |
| if (snapshot->clientChanges & layer_state_t::eDefaultFrameRateCompatibilityChanged) { |
| mScheduler->setDefaultFrameRateCompatibility(snapshot->sequence, |
| snapshot->defaultFrameRateCompatibility); |
| } |
| |
| if (snapshot->changes.test(Changes::Animation)) { |
| it->second->recordLayerHistoryAnimationTx(layerProps, now); |
| } |
| |
| if (snapshot->changes.test(Changes::FrameRate)) { |
| it->second->setFrameRateForLayerTree(snapshot->frameRate, layerProps, now); |
| } |
| |
| if (snapshot->changes.test(Changes::Buffer)) { |
| it->second->recordLayerHistoryBufferUpdate(layerProps, now); |
| } |
| } |
| } |
| |
| bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, |
| bool flushTransactions, bool& outTransactionsAreEmpty) { |
| using Changes = frontend::RequestedLayerState::Changes; |
| ATRACE_CALL(); |
| frontend::Update update; |
| if (flushTransactions) { |
| ATRACE_NAME("TransactionHandler:flushTransactions"); |
| // Locking: |
| // 1. to prevent onHandleDestroyed from being called while the state lock is held, |
| // we must keep a copy of the transactions (specifically the composer |
| // states) around outside the scope of the lock. |
| // 2. Transactions and created layers do not share a lock. To prevent applying |
| // transactions with layers still in the createdLayer queue, collect the transactions |
| // before committing the created layers. |
| // 3. Transactions can only be flushed after adding layers, since the layer can be a newly |
| // created one |
| mTransactionHandler.collectTransactions(); |
| { |
| // TODO(b/238781169) lockless queue this and keep order. |
| std::scoped_lock<std::mutex> lock(mCreatedLayersLock); |
| update.layerCreatedStates = std::move(mCreatedLayers); |
| mCreatedLayers.clear(); |
| update.newLayers = std::move(mNewLayers); |
| mNewLayers.clear(); |
| update.layerCreationArgs = std::move(mNewLayerArgs); |
| mNewLayerArgs.clear(); |
| update.destroyedHandles = std::move(mDestroyedHandles); |
| mDestroyedHandles.clear(); |
| } |
| |
| mLayerLifecycleManager.addLayers(std::move(update.newLayers)); |
| update.transactions = mTransactionHandler.flushTransactions(); |
| if (mTransactionTracing) { |
| mTransactionTracing->addCommittedTransactions(ftl::to_underlying(vsyncId), frameTimeNs, |
| update, mFrontEndDisplayInfos, |
| mFrontEndDisplayInfosChanged); |
| } |
| mLayerLifecycleManager.applyTransactions(update.transactions); |
| mLayerLifecycleManager.onHandlesDestroyed(update.destroyedHandles); |
| for (auto& legacyLayer : update.layerCreatedStates) { |
| sp<Layer> layer = legacyLayer.layer.promote(); |
| if (layer) { |
| mLegacyLayers[layer->sequence] = layer; |
| } |
| } |
| mLayerHierarchyBuilder.update(mLayerLifecycleManager); |
| } |
| |
| // Keep a copy of the drawing state (that is going to be overwritten |
| // by commitTransactionsLocked) outside of mStateLock so that the side |
| // effects of the State assignment don't happen with mStateLock held, |
| // which can cause deadlocks. |
| State drawingState(mDrawingState); |
| Mutex::Autolock lock(mStateLock); |
| bool mustComposite = false; |
| mustComposite |= applyAndCommitDisplayTransactionStatesLocked(update.transactions); |
| |
| { |
| ATRACE_NAME("LayerSnapshotBuilder:update"); |
| frontend::LayerSnapshotBuilder::Args |
| args{.root = mLayerHierarchyBuilder.getHierarchy(), |
| .layerLifecycleManager = mLayerLifecycleManager, |
| .includeMetadata = mCompositionEngine->getFeatureFlags().test( |
| compositionengine::Feature::kSnapshotLayerMetadata), |
| .displays = mFrontEndDisplayInfos, |
| .displayChanges = mFrontEndDisplayInfosChanged, |
| .globalShadowSettings = mDrawingState.globalShadowSettings, |
| .supportsBlur = mSupportsBlur, |
| .forceFullDamage = mForceFullDamage, |
| .supportedLayerGenericMetadata = |
| getHwComposer().getSupportedLayerGenericMetadata(), |
| .genericLayerMetadataKeyMap = getGenericLayerMetadataKeyMap(), |
| .skipRoundCornersWhenProtected = |
| !getRenderEngine().supportsProtectedContent()}; |
| mLayerSnapshotBuilder.update(args); |
| } |
| |
| if (mLayerLifecycleManager.getGlobalChanges().any(Changes::Geometry | Changes::Input | |
| Changes::Hierarchy | Changes::Visibility)) { |
| mUpdateInputInfo = true; |
| } |
| if (mLayerLifecycleManager.getGlobalChanges().any(Changes::VisibleRegion | Changes::Hierarchy | |
| Changes::Visibility | Changes::Geometry)) { |
| mVisibleRegionsDirty = true; |
| } |
| if (mLayerLifecycleManager.getGlobalChanges().any(Changes::Hierarchy | Changes::FrameRate)) { |
| // The frame rate of attached choreographers can only change as a result of a |
| // FrameRate change (including when Hierarchy changes). |
| mUpdateAttachedChoreographer = true; |
| } |
| outTransactionsAreEmpty = mLayerLifecycleManager.getGlobalChanges().get() == 0; |
| if (FlagManager::getInstance().vrr_bugfix_24q4()) { |
| mustComposite |= mLayerLifecycleManager.getGlobalChanges().any( |
| frontend::RequestedLayerState::kMustComposite); |
| } else { |
| mustComposite |= mLayerLifecycleManager.getGlobalChanges().get() != 0; |
| } |
| |
| bool newDataLatched = false; |
| ATRACE_NAME("DisplayCallbackAndStatsUpdates"); |
| mustComposite |= applyTransactionsLocked(update.transactions); |
| traverseLegacyLayers([&](Layer* layer) { layer->commitTransaction(); }); |
| const nsecs_t latchTime = systemTime(); |
| bool unused = false; |
| |
| for (auto& layer : mLayerLifecycleManager.getLayers()) { |
| if (layer->changes.test(frontend::RequestedLayerState::Changes::Created) && |
| layer->bgColorLayer) { |
| sp<Layer> bgColorLayer = getFactory().createEffectLayer( |
| LayerCreationArgs(this, nullptr, layer->name, |
| ISurfaceComposerClient::eFXSurfaceEffect, LayerMetadata(), |
| std::make_optional(layer->id), true)); |
| mLegacyLayers[bgColorLayer->sequence] = bgColorLayer; |
| } |
| const bool willReleaseBufferOnLatch = layer->willReleaseBufferOnLatch(); |
| |
| auto it = mLegacyLayers.find(layer->id); |
| if (it == mLegacyLayers.end() && |
| layer->changes.test(frontend::RequestedLayerState::Changes::Destroyed)) { |
| // Layer handle was created and immediately destroyed. It was destroyed before it |
| // was added to the map. |
| continue; |
| } |
| |
| LLOG_ALWAYS_FATAL_WITH_TRACE_IF(it == mLegacyLayers.end(), |
| "Couldnt find layer object for %s", |
| layer->getDebugString().c_str()); |
| if (!layer->hasReadyFrame() && !willReleaseBufferOnLatch) { |
| if (!it->second->hasBuffer()) { |
| // The last latch time is used to classify a missed frame as buffer stuffing |
| // instead of a missed frame. This is used to identify scenarios where we |
| // could not latch a buffer or apply a transaction due to backpressure. |
| // We only update the latch time for buffer less layers here, the latch time |
| // is updated for buffer layers when the buffer is latched. |
| it->second->updateLastLatchTime(latchTime); |
| } |
| continue; |
| } |
| |
| const bool bgColorOnly = |
| !layer->externalTexture && (layer->bgColorLayerId != UNASSIGNED_LAYER_ID); |
| if (willReleaseBufferOnLatch) { |
| mLayersWithBuffersRemoved.emplace(it->second); |
| } |
| it->second->latchBufferImpl(unused, latchTime, bgColorOnly); |
| newDataLatched = true; |
| |
| mLayersWithQueuedFrames.emplace(it->second); |
| mLayersIdsWithQueuedFrames.emplace(it->second->sequence); |
| } |
| |
| updateLayerHistory(latchTime); |
| mLayerSnapshotBuilder.forEachVisibleSnapshot([&](const frontend::LayerSnapshot& snapshot) { |
| if (mLayersIdsWithQueuedFrames.find(snapshot.path.id) == mLayersIdsWithQueuedFrames.end()) |
| return; |
| Region visibleReg; |
| visibleReg.set(snapshot.transformedBoundsWithoutTransparentRegion); |
| invalidateLayerStack(snapshot.outputFilter, visibleReg); |
| }); |
| |
| for (auto& destroyedLayer : mLayerLifecycleManager.getDestroyedLayers()) { |
| mLegacyLayers.erase(destroyedLayer->id); |
| } |
| |
| { |
| ATRACE_NAME("LayerLifecycleManager:commitChanges"); |
| mLayerLifecycleManager.commitChanges(); |
| } |
| |
| // enter boot animation on first buffer latch |
| if (CC_UNLIKELY(mBootStage == BootStage::BOOTLOADER && newDataLatched)) { |
| ALOGI("Enter boot animation"); |
| mBootStage = BootStage::BOOTANIMATION; |
| } |
| |
| mustComposite |= (getTransactionFlags() & ~eTransactionFlushNeeded) || newDataLatched; |
| if (mustComposite) { |
| commitTransactions(); |
| } |
| |
| return mustComposite; |
| } |
| |
| bool SurfaceFlinger::commit(PhysicalDisplayId pacesetterId, |
| const scheduler::FrameTargets& frameTargets) { |
| const scheduler::FrameTarget& pacesetterFrameTarget = *frameTargets.get(pacesetterId)->get(); |
| |
| const VsyncId vsyncId = pacesetterFrameTarget.vsyncId(); |
| ATRACE_NAME(ftl::Concat(__func__, ' ', ftl::to_underlying(vsyncId)).c_str()); |
| |
| if (pacesetterFrameTarget.didMissFrame()) { |
| mTimeStats->incrementMissedFrames(); |
| } |
| |
| // If a mode set is pending and the fence hasn't fired yet, wait for the next commit. |
| if (std::any_of(frameTargets.begin(), frameTargets.end(), |
| [this](const auto& pair) FTL_FAKE_GUARD(kMainThreadContext) { |
| const auto [displayId, target] = pair; |
| return target->isFramePending() && |
| mDisplayModeController.isModeSetPending(displayId); |
| })) { |
| mScheduler->scheduleFrame(); |
| return false; |
| } |
| |
| { |
| ConditionalLock lock(mStateLock, FlagManager::getInstance().connected_display()); |
| |
| for (const auto [displayId, _] : frameTargets) { |
| if (mDisplayModeController.isModeSetPending(displayId)) { |
| finalizeDisplayModeChange(displayId); |
| } |
| } |
| } |
| |
| if (pacesetterFrameTarget.isFramePending()) { |
| if (mBackpressureGpuComposition || pacesetterFrameTarget.didMissHwcFrame()) { |
| if (FlagManager::getInstance().vrr_config()) { |
| mScheduler->getVsyncSchedule()->getTracker().onFrameMissed( |
| pacesetterFrameTarget.expectedPresentTime()); |
| } |
| scheduleCommit(FrameHint::kNone); |
| return false; |
| } |
| } |
| |
| const Period vsyncPeriod = mScheduler->getVsyncSchedule()->period(); |
| |
| // Save this once per commit + composite to ensure consistency |
| // TODO (b/240619471): consider removing active display check once AOD is fixed |
| const auto activeDisplay = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(mActiveDisplayId)); |
| mPowerHintSessionEnabled = mPowerAdvisor->usePowerHintSession() && activeDisplay && |
| activeDisplay->getPowerMode() == hal::PowerMode::ON; |
| if (mPowerHintSessionEnabled) { |
| mPowerAdvisor->setCommitStart(pacesetterFrameTarget.frameBeginTime()); |
| mPowerAdvisor->setExpectedPresentTime(pacesetterFrameTarget.expectedPresentTime()); |
| |
| // Frame delay is how long we should have minus how long we actually have. |
| const Duration idealSfWorkDuration = |
| mScheduler->vsyncModulator().getVsyncConfig().sfWorkDuration; |
| const Duration frameDelay = |
| idealSfWorkDuration - pacesetterFrameTarget.expectedFrameDuration(); |
| |
| mPowerAdvisor->setFrameDelay(frameDelay); |
| mPowerAdvisor->setTotalFrameTargetWorkDuration(idealSfWorkDuration); |
| |
| const Period idealVsyncPeriod = |
| mDisplayModeController.getActiveMode(pacesetterId).fps.getPeriod(); |
| mPowerAdvisor->updateTargetWorkDuration(idealVsyncPeriod); |
| } |
| |
| if (mRefreshRateOverlaySpinner || mHdrSdrRatioOverlay) { |
| Mutex::Autolock lock(mStateLock); |
| if (const auto display = getDefaultDisplayDeviceLocked()) { |
| display->animateOverlay(); |
| } |
| } |
| |
| // Composite if transactions were committed, or if requested by HWC. |
| bool mustComposite = mMustComposite.exchange(false); |
| { |
| mFrameTimeline->setSfWakeUp(ftl::to_underlying(vsyncId), |
| pacesetterFrameTarget.frameBeginTime().ns(), |
| Fps::fromPeriodNsecs(vsyncPeriod.ns()), |
| mScheduler->getPacesetterRefreshRate()); |
| |
| const bool flushTransactions = clearTransactionFlags(eTransactionFlushNeeded); |
| bool transactionsAreEmpty = false; |
| if (mLayerLifecycleManagerEnabled) { |
| mustComposite |= |
| updateLayerSnapshots(vsyncId, pacesetterFrameTarget.frameBeginTime().ns(), |
| flushTransactions, transactionsAreEmpty); |
| } |
| |
| // Tell VsyncTracker that we are going to present this frame before scheduling |
| // setTransactionFlags which will schedule another SF frame. This was if the tracker |
| // needs to adjust the vsync timeline, it will be done before the next frame. |
| if (FlagManager::getInstance().vrr_config() && mustComposite) { |
| mScheduler->getVsyncSchedule()->getTracker().onFrameBegin( |
| pacesetterFrameTarget.expectedPresentTime(), |
| pacesetterFrameTarget.lastSignaledFrameTime()); |
| } |
| if (transactionFlushNeeded()) { |
| setTransactionFlags(eTransactionFlushNeeded); |
| } |
| |
| // This has to be called after latchBuffers because we want to include the layers that have |
| // been latched in the commit callback |
| if (transactionsAreEmpty) { |
| // Invoke empty transaction callbacks early. |
| mTransactionCallbackInvoker.sendCallbacks(false /* onCommitOnly */); |
| } else { |
| // Invoke OnCommit callbacks. |
| mTransactionCallbackInvoker.sendCallbacks(true /* onCommitOnly */); |
| } |
| } |
| |
| // Layers need to get updated (in the previous line) before we can use them for |
| // choosing the refresh rate. |
| // Hold mStateLock as chooseRefreshRateForContent promotes wp<Layer> to sp<Layer> |
| // and may eventually call to ~Layer() if it holds the last reference |
| { |
| bool updateAttachedChoreographer = mUpdateAttachedChoreographer; |
| mUpdateAttachedChoreographer = false; |
| |
| Mutex::Autolock lock(mStateLock); |
| mScheduler->chooseRefreshRateForContent(mLayerLifecycleManagerEnabled |
| ? &mLayerHierarchyBuilder.getHierarchy() |
| : nullptr, |
| updateAttachedChoreographer); |
| |
| if (FlagManager::getInstance().connected_display()) { |
| initiateDisplayModeChanges(); |
| } |
| } |
| |
| if (!FlagManager::getInstance().connected_display()) { |
| ftl::FakeGuard guard(mStateLock); |
| initiateDisplayModeChanges(); |
| } |
| |
| updateCursorAsync(); |
| if (!mustComposite) { |
| updateInputFlinger(vsyncId, pacesetterFrameTarget.frameBeginTime()); |
| } |
| doActiveLayersTracingIfNeeded(false, mVisibleRegionsDirty, |
| pacesetterFrameTarget.frameBeginTime(), vsyncId); |
| |
| mLastCommittedVsyncId = vsyncId; |
| |
| persistDisplayBrightness(mustComposite); |
| |
| return mustComposite && CC_LIKELY(mBootStage != BootStage::BOOTLOADER); |
| } |
| |
| CompositeResultsPerDisplay SurfaceFlinger::composite( |
| PhysicalDisplayId pacesetterId, const scheduler::FrameTargeters& frameTargeters) { |
| const scheduler::FrameTarget& pacesetterTarget = |
| frameTargeters.get(pacesetterId)->get()->target(); |
| |
| const VsyncId vsyncId = pacesetterTarget.vsyncId(); |
| ATRACE_NAME(ftl::Concat(__func__, ' ', ftl::to_underlying(vsyncId)).c_str()); |
| |
| compositionengine::CompositionRefreshArgs refreshArgs; |
| refreshArgs.powerCallback = this; |
| const auto& displays = FTL_FAKE_GUARD(mStateLock, mDisplays); |
| refreshArgs.outputs.reserve(displays.size()); |
| |
| // Add outputs for physical displays. |
| for (const auto& [id, targeter] : frameTargeters) { |
| ftl::FakeGuard guard(mStateLock); |
| |
| if (const auto display = getCompositionDisplayLocked(id)) { |
| refreshArgs.outputs.push_back(display); |
| } |
| |
| refreshArgs.frameTargets.try_emplace(id, &targeter->target()); |
| } |
| |
| std::vector<DisplayId> displayIds; |
| for (const auto& [_, display] : displays) { |
| displayIds.push_back(display->getId()); |
| display->tracePowerMode(); |
| |
| // Add outputs for virtual displays. |
| if (display->isVirtual()) { |
| const Fps re
|