diff options
author | 2020-10-13 12:33:42 +0200 | |
---|---|---|
committer | 2020-11-16 18:01:55 +0100 | |
commit | 4608442db90c07dccc833dbdf1cea78f57c7b96d (patch) | |
tree | be976923f779a78bf40ee5f5165a51f76b37b897 | |
parent | 6bb6836cbdc56efc7e3bc4a14aea6dcf43f9ff3c (diff) |
Add shouldBeSeamless to setFrameRate
This CL adds a new parameter shouldBeSeamless to the existing
setFrameRate APIs. This parameter indicates whether the desired
refresh rate should be achieved only seamlessly or also switches
with visual interruptions for the user are allowed. The default
value of the new parameter is "true".
Test: atest RefreshRateConfigsTest
Test: atest SetFrameRateTest
Test: atest libsurfaceflinger_unittest
Test: atest libgui_test
Bug: 161776961
Change-Id: I0df16e09f77c8c198fd3733fb581a2aaadfed685
33 files changed, 402 insertions, 124 deletions
diff --git a/include/android/surface_control.h b/include/android/surface_control.h index cbcf6ec5c0..9758c9c8c3 100644 --- a/include/android/surface_control.h +++ b/include/android/surface_control.h @@ -410,7 +410,23 @@ void ASurfaceTransaction_setHdrMetadata_cta861_3(ASurfaceTransaction* transactio #if __ANDROID_API__ >= 30 /** - * Sets the intended frame rate for |surface_control|. + * Same as ASurfaceTransaction_setFrameRateWithSeamlessness(transaction, surface_control, + * frameRate, compatibility, true). + * + * See ASurfaceTransaction_setFrameRateWithSeamlessness(). + * + * Available since API level 30. + */ +void ASurfaceTransaction_setFrameRate(ASurfaceTransaction* transaction, + ASurfaceControl* surface_control, float frameRate, + int8_t compatibility) __INTRODUCED_IN(30); + +#endif // __ANDROID_API__ >= 30 + +#if __ANDROID_API__ >= 31 + +/** + * Sets the intended frame rate for \a surface_control. * * On devices that are capable of running the display at different refresh rates, the system may * choose a display refresh rate to better match this surface's frame rate. Usage of this API won't @@ -419,24 +435,30 @@ void ASurfaceTransaction_setHdrMetadata_cta861_3(ASurfaceTransaction* transactio * callback timings, and changes to the time interval at which the system releases buffers back to * the application. * - * |frameRate| is the intended frame rate of this surface, in frames per second. 0 is a special + * \param frameRate is the intended frame rate of this surface, in frames per second. 0 is a special * value that indicates the app will accept the system's choice for the display frame rate, which is * the default behavior if this function isn't called. The frameRate param does <em>not</em> need to * be a valid refresh rate for this device's display - e.g., it's fine to pass 30fps to a device * that can only run the display at 60fps. * - * |compatibility| The frame rate compatibility of this surface. The compatibility value may + * \param compatibility The frame rate compatibility of this surface. The compatibility value may * influence the system's choice of display frame rate. To specify a compatibility use the * ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* enum. * - * Available since API level 30. + * \param shouldBeSeamless Whether display refresh rate transitions should be seamless. A + * seamless transition is one that doesn't have any visual interruptions, such as a black + * screen for a second or two. True indicates that any frame rate changes caused by this + * request should be seamless. False indicates that non-seamless refresh rates are also + * acceptable. + * + * Available since API level 31. */ -void ASurfaceTransaction_setFrameRate(ASurfaceTransaction* transaction, +void ASurfaceTransaction_setFrameRateWithSeamlessness(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, float frameRate, - int8_t compatibility) __INTRODUCED_IN(30); - -#endif // __ANDROID_API__ >= 30 + int8_t compatibility, bool shouldBeSeamless) + __INTRODUCED_IN(31); +#endif // __ANDROID_API__ >= 31 __END_DECLS #endif // ANDROID_SURFACE_CONTROL_H diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 678613b1ff..ac1c7369b6 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -378,11 +378,11 @@ public: }).detach(); } - status_t setFrameRate(float frameRate, int8_t compatibility) override { + status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless) override { if (!ValidateFrameRate(frameRate, compatibility, "BBQSurface::setFrameRate")) { return BAD_VALUE; } - return mBbq->setFrameRate(frameRate, compatibility); + return mBbq->setFrameRate(frameRate, compatibility, shouldBeSeamless); } status_t setFrameTimelineVsync(int64_t frameTimelineVsyncId) override { @@ -392,12 +392,12 @@ public: // TODO: Can we coalesce this with frame updates? Need to confirm // no timing issues. -status_t BLASTBufferQueue::setFrameRate(float frameRate, int8_t compatibility) { +status_t BLASTBufferQueue::setFrameRate(float frameRate, int8_t compatibility, + bool shouldBeSeamless) { std::unique_lock _lock{mMutex}; SurfaceComposerClient::Transaction t; - return t.setFrameRate(mSurfaceControl, frameRate, compatibility) - .apply(); + return t.setFrameRate(mSurfaceControl, frameRate, compatibility, shouldBeSeamless).apply(); } status_t BLASTBufferQueue::setFrameTimelineVsync(int64_t frameTimelineVsyncId) { diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index 6f92233935..a9fe5bf319 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -1114,7 +1114,7 @@ public: } virtual status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate, - int8_t compatibility) { + int8_t compatibility, bool shouldBeSeamless) { Parcel data, reply; status_t err = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (err != NO_ERROR) { @@ -1140,6 +1140,12 @@ public: return err; } + err = data.writeBool(shouldBeSeamless); + if (err != NO_ERROR) { + ALOGE("setFrameRate: failed writing bool: %s (%d)", strerror(-err), -err); + return err; + } + err = remote()->transact(BnSurfaceComposer::SET_FRAME_RATE, data, &reply); if (err != NO_ERROR) { ALOGE("setFrameRate: failed to transact: %s (%d)", strerror(-err), err); @@ -2033,7 +2039,13 @@ status_t BnSurfaceComposer::onTransact( ALOGE("setFrameRate: failed to read byte: %s (%d)", strerror(-err), -err); return err; } - status_t result = setFrameRate(surface, frameRate, compatibility); + bool shouldBeSeamless; + err = data.readBool(&shouldBeSeamless); + if (err != NO_ERROR) { + ALOGE("setFrameRate: failed to read bool: %s (%d)", strerror(-err), -err); + return err; + } + status_t result = setFrameRate(surface, frameRate, compatibility, shouldBeSeamless); reply->writeInt32(result); return NO_ERROR; } diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 9722f368f4..90999faa78 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -59,6 +59,7 @@ layer_state_t::layer_state_t() frameRateSelectionPriority(-1), frameRate(0.0f), frameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT), + shouldBeSeamless(true), fixedTransformHint(ui::Transform::ROT_INVALID), frameNumber(0) { matrix.dsdx = matrix.dtdy = 1.0f; @@ -144,6 +145,7 @@ status_t layer_state_t::write(Parcel& output) const SAFE_PARCEL(output.writeInt32, frameRateSelectionPriority); SAFE_PARCEL(output.writeFloat, frameRate); SAFE_PARCEL(output.writeByte, frameRateCompatibility); + SAFE_PARCEL(output.writeBool, shouldBeSeamless); SAFE_PARCEL(output.writeUint32, fixedTransformHint); SAFE_PARCEL(output.writeUint64, frameNumber); SAFE_PARCEL(output.writeInt64, frameTimelineVsyncId); @@ -262,6 +264,7 @@ status_t layer_state_t::read(const Parcel& input) SAFE_PARCEL(input.readInt32, &frameRateSelectionPriority); SAFE_PARCEL(input.readFloat, &frameRate); SAFE_PARCEL(input.readByte, &frameRateCompatibility); + SAFE_PARCEL(input.readBool, &shouldBeSeamless); SAFE_PARCEL(input.readUint32, &tmpUint32); fixedTransformHint = static_cast<ui::Transform::RotationFlags>(tmpUint32); SAFE_PARCEL(input.readUint64, &frameNumber); @@ -521,6 +524,7 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eFrameRateChanged; frameRate = other.frameRate; frameRateCompatibility = other.frameRateCompatibility; + shouldBeSeamless = other.shouldBeSeamless; } if (other.what & eFixedTransformHintChanged) { what |= eFixedTransformHintChanged; diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index c1155ab73a..94390aa86c 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -1443,7 +1443,8 @@ int Surface::dispatchGetLastQueueDuration(va_list args) { int Surface::dispatchSetFrameRate(va_list args) { float frameRate = static_cast<float>(va_arg(args, double)); int8_t compatibility = static_cast<int8_t>(va_arg(args, int)); - return setFrameRate(frameRate, compatibility); + bool shouldBeSeamless = static_cast<bool>(va_arg(args, int)); + return setFrameRate(frameRate, compatibility, shouldBeSeamless); } int Surface::dispatchAddCancelInterceptor(va_list args) { @@ -2279,7 +2280,7 @@ void Surface::ProducerListenerProxy::onBuffersDiscarded(const std::vector<int32_ mSurfaceListener->onBuffersDiscarded(discardedBufs); } -status_t Surface::setFrameRate(float frameRate, int8_t compatibility) { +status_t Surface::setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless) { ATRACE_CALL(); ALOGV("Surface::setFrameRate"); @@ -2287,7 +2288,8 @@ status_t Surface::setFrameRate(float frameRate, int8_t compatibility) { return BAD_VALUE; } - return composerService()->setFrameRate(mGraphicBufferProducer, frameRate, compatibility); + return composerService()->setFrameRate(mGraphicBufferProducer, frameRate, compatibility, + shouldBeSeamless); } status_t Surface::setFrameTimelineVsync(int64_t frameTimelineVsyncId) { diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 039e9008e8..a822598d82 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -1474,7 +1474,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setShado } SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameRate( - const sp<SurfaceControl>& sc, float frameRate, int8_t compatibility) { + const sp<SurfaceControl>& sc, float frameRate, int8_t compatibility, + bool shouldBeSeamless) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; @@ -1487,6 +1488,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrame s->what |= layer_state_t::eFrameRateChanged; s->frameRate = frameRate; s->frameRateCompatibility = compatibility; + s->shouldBeSeamless = shouldBeSeamless; return *this; } diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 2300e81aa7..7741d8c38a 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -85,7 +85,7 @@ public: void update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height); void flushShadowQueue() { mFlushShadowQueue = true; } - status_t setFrameRate(float frameRate, int8_t compatibility); + status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless); status_t setFrameTimelineVsync(int64_t frameTimelineVsyncId); virtual ~BLASTBufferQueue() = default; diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index 5cd9356449..9e96b79b8c 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -475,7 +475,7 @@ public: * Sets the intended frame rate for a surface. See ANativeWindow_setFrameRate() for more info. */ virtual status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate, - int8_t compatibility) = 0; + int8_t compatibility, bool shouldBeSeamless) = 0; /* * Acquire a frame rate flexibility token from SurfaceFlinger. While this token is acquired, diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index a73d9a68a5..d9f280684f 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -218,6 +218,7 @@ struct layer_state_t { // Layer frame rate and compatibility. See ANativeWindow_setFrameRate(). float frameRate; int8_t frameRateCompatibility; + bool shouldBeSeamless; // Set by window manager indicating the layer and all its children are // in a different orientation than the display. The hint suggests that diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h index 4aa076e7b2..82bc5c9efb 100644 --- a/libs/gui/include/gui/Surface.h +++ b/libs/gui/include/gui/Surface.h @@ -186,7 +186,7 @@ public: status_t getUniqueId(uint64_t* outId) const; status_t getConsumerUsage(uint64_t* outUsage) const; - virtual status_t setFrameRate(float frameRate, int8_t compatibility); + virtual status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless); virtual status_t setFrameTimelineVsync(int64_t frameTimelineVsyncId); protected: diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 73909a30bb..6289c6a3cd 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -524,7 +524,7 @@ public: Transaction& setShadowRadius(const sp<SurfaceControl>& sc, float cornerRadius); Transaction& setFrameRate(const sp<SurfaceControl>& sc, float frameRate, - int8_t compatibility); + int8_t compatibility, bool shouldBeSeamless); // Set by window manager indicating the layer and all its children are // in a different orientation than the display. The hint suggests that diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 0cd3962aa1..2392ae5ccd 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -869,7 +869,7 @@ public: } status_t setFrameRate(const sp<IGraphicBufferProducer>& /*surface*/, float /*frameRate*/, - int8_t /*compatibility*/) override { + int8_t /*compatibility*/, bool /*shouldBeSeamless*/) override { return NO_ERROR; } diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp index fd1793b6bc..b406a9c2fe 100644 --- a/libs/nativewindow/ANativeWindow.cpp +++ b/libs/nativewindow/ANativeWindow.cpp @@ -159,10 +159,8 @@ int32_t ANativeWindow_getBuffersDataSpace(ANativeWindow* window) { } int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate, int8_t compatibility) { - if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) { - return -EINVAL; - } - return native_window_set_frame_rate(window, frameRate, compatibility); + return ANativeWindow_setFrameRateWithSeamlessness(window, frameRate, compatibility, + /*shouldBeSeamless*/ true); } void ANativeWindow_tryAllocateBuffers(ANativeWindow* window) { @@ -172,6 +170,13 @@ void ANativeWindow_tryAllocateBuffers(ANativeWindow* window) { window->perform(window, NATIVE_WINDOW_ALLOCATE_BUFFERS); } +int32_t ANativeWindow_setFrameRateWithSeamlessness(ANativeWindow* window, float frameRate, + int8_t compatibility, bool shouldBeSeamless) { + if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) { + return -EINVAL; + } + return native_window_set_frame_rate(window, frameRate, compatibility, shouldBeSeamless); +} /************************************************************************************************** * vndk-stable diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h index 36aad2eced..deea59b9fb 100644 --- a/libs/nativewindow/include/android/native_window.h +++ b/libs/nativewindow/include/android/native_window.h @@ -34,6 +34,7 @@ #define ANDROID_NATIVE_WINDOW_H #include <stdint.h> +#include <stdbool.h> #include <sys/cdefs.h> #include <android/data_space.h> @@ -256,6 +257,31 @@ enum ANativeWindow_FrameRateCompatibility { }; /** + * Same as ANativeWindow_setFrameRateWithSeamlessness(window, frameRate, compatibility, true). + * + * See ANativeWindow_setFrameRateWithSeamlessness(). + * + * Available since API level 30. + */ +int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate, int8_t compatibility) + __INTRODUCED_IN(30); + +/** + * Provides a hint to the window that buffers should be preallocated ahead of + * time. Note that the window implementation is not guaranteed to preallocate + * any buffers, for instance if an implementation disallows allocation of new + * buffers, or if there is insufficient memory in the system to preallocate + * additional buffers + * + * Available since API level 30. + */ +void ANativeWindow_tryAllocateBuffers(ANativeWindow* window); + +#endif // __ANDROID_API__ >= 30 + +#if __ANDROID_API__ >= 31 + +/** * Sets the intended frame rate for this window. * * On devices that are capable of running the display at different refresh @@ -271,7 +297,7 @@ enum ANativeWindow_FrameRateCompatibility { * this ANativeWindow is consumed by something other than the system compositor, * e.g. a media codec, this call has no effect. * - * Available since API level 30. + * Available since API level 31. * * \param frameRate The intended frame rate of this window, in frames per * second. 0 is a special value that indicates the app will accept the system's @@ -284,24 +310,19 @@ enum ANativeWindow_FrameRateCompatibility { * compatibility value may influence the system's choice of display refresh * rate. See the ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* values for more info. * + * \param shouldBeSeamless Whether display refresh rate transitions should be seamless. A + * seamless transition is one that doesn't have any visual interruptions, such as a black + * screen for a second or two. True indicates that any frame rate changes caused by this + * request should be seamless. False indicates that non-seamless refresh rates are also + * acceptable. + * * \return 0 for success, -EINVAL if the window, frame rate, or compatibility * value are invalid. */ -int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate, int8_t compatibility) - __INTRODUCED_IN(30); +int32_t ANativeWindow_setFrameRateWithSeamlessness(ANativeWindow* window, float frameRate, + int8_t compatibility, bool shouldBeSeamless) __INTRODUCED_IN(31); -/** - * Provides a hint to the window that buffers should be preallocated ahead of - * time. Note that the window implementation is not guaranteed to preallocate - * any buffers, for instance if an implementation disallows allocation of new - * buffers, or if there is insufficient memory in the system to preallocate - * additional buffers - * - * Available since API level 30. - */ -void ANativeWindow_tryAllocateBuffers(ANativeWindow* window); - -#endif // __ANDROID_API__ >= 30 +#endif // __ANDROID_API__ >= 31 #ifdef __cplusplus }; diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h index 138e08f490..82d2e661b4 100644 --- a/libs/nativewindow/include/system/window.h +++ b/libs/nativewindow/include/system/window.h @@ -1018,9 +1018,9 @@ static inline int native_window_set_auto_prerotation(struct ANativeWindow* windo } static inline int native_window_set_frame_rate(struct ANativeWindow* window, float frameRate, - int8_t compatibility) { + int8_t compatibility, bool shouldBeSeamless) { return window->perform(window, NATIVE_WINDOW_SET_FRAME_RATE, (double)frameRate, - (int)compatibility); + (int)compatibility, (int)shouldBeSeamless); } static inline int native_window_set_frame_timeline_vsync(struct ANativeWindow* window, diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt index 1b5d20dff7..de48ec25dd 100644 --- a/libs/nativewindow/libnativewindow.map.txt +++ b/libs/nativewindow/libnativewindow.map.txt @@ -46,6 +46,7 @@ LIBNATIVEWINDOW { ANativeWindow_setBuffersTransform; ANativeWindow_setDequeueTimeout; # apex # introduced=30 ANativeWindow_setFrameRate; # introduced=30 + ANativeWindow_setFrameRateWithSeamlessness; # introduced=31 ANativeWindow_setSharedBufferMode; # llndk ANativeWindow_setSwapInterval; # llndk ANativeWindow_setUsage; # llndk diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 94819664ce..79e2ad0dbd 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -1687,8 +1687,9 @@ void Layer::miniDump(std::string& result, const DisplayDevice& display) const { crop.bottom); if (layerState.frameRate.rate != 0 || layerState.frameRate.type != FrameRateCompatibility::Default) { - StringAppendF(&result, "% 6.2ffps %15s", layerState.frameRate.rate, - frameRateCompatibilityString(layerState.frameRate.type).c_str()); + StringAppendF(&result, "% 6.2ffps %15s seamless=%d", layerState.frameRate.rate, + frameRateCompatibilityString(layerState.frameRate.type).c_str(), + layerState.frameRate.shouldBeSeamless); } else { StringAppendF(&result, " "); } @@ -2750,6 +2751,12 @@ bool Layer::getPrimaryDisplayOnly() const { // --------------------------------------------------------------------------- +std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate) { + return stream << "{rate=" << rate.rate + << " type=" << Layer::frameRateCompatibilityString(rate.type) + << " shouldBeSeamless=" << rate.shouldBeSeamless << "}"; +} + }; // namespace android #if defined(__gl_h_) diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index b1ab9ec306..1a784aa778 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -153,12 +153,15 @@ public: struct FrameRate { float rate; FrameRateCompatibility type; + bool shouldBeSeamless; - FrameRate() : rate(0), type(FrameRateCompatibility::Default) {} - FrameRate(float rate, FrameRateCompatibility type) : rate(rate), type(type) {} + FrameRate() : rate(0), type(FrameRateCompatibility::Default), shouldBeSeamless(true) {} + FrameRate(float rate, FrameRateCompatibility type, bool shouldBeSeamless = true) + : rate(rate), type(type), shouldBeSeamless(shouldBeSeamless) {} bool operator==(const FrameRate& other) const { - return rate == other.rate && type == other.type; + return rate == other.rate && type == other.type && + shouldBeSeamless == other.shouldBeSeamless; } bool operator!=(const FrameRate& other) const { return !(*this == other); } @@ -1126,4 +1129,6 @@ private: const std::vector<BlurRegion>& getBlurRegions() const; }; +std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate); + } // namespace android diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp index 36433c20ed..28af930bbe 100644 --- a/services/surfaceflinger/Scheduler/LayerHistory.cpp +++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp @@ -125,12 +125,23 @@ LayerHistory::Summary LayerHistory::summarize(nsecs_t now) { return LayerVoteType::NoVote; } }(); - summary.push_back({layer->getName(), voteType, frameRate.rate, /* weight */ 1.0f, - layerFocused}); + summary.push_back( + RefreshRateConfigs::LayerRequirement{.name = layer->getName(), + .vote = voteType, + .desiredRefreshRate = frameRate.rate, + .shouldBeSeamless = + frameRate.shouldBeSeamless, + .weight = 1.0f, + .focused = layerFocused}); } else if (recent) { - summary.push_back({layer->getName(), LayerVoteType::Heuristic, - info->getRefreshRate(now), - /* weight */ 1.0f, layerFocused}); + summary.push_back( + RefreshRateConfigs::LayerRequirement{.name = layer->getName(), + .vote = LayerVoteType::Heuristic, + .desiredRefreshRate = + info->getRefreshRate(now), + .shouldBeSeamless = true, + .weight = 1.0f, + .focused = layerFocused}); } if (CC_UNLIKELY(mTraceEnabled)) { diff --git a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp index 37e67e17fe..a63ccc1df0 100644 --- a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp +++ b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp @@ -130,9 +130,9 @@ LayerHistoryV2::Summary LayerHistoryV2::summarize(nsecs_t now) { ALOGV("%s has priority: %d %s focused", strong->getName().c_str(), frameRateSelectionPriority, layerFocused ? "" : "not"); - const auto [type, refreshRate] = info->getRefreshRate(now); + const auto vote = info->getRefreshRateVote(now); // Skip NoVote layer as those don't have any requirements - if (type == LayerHistory::LayerVoteType::NoVote) { + if (vote.type == LayerHistory::LayerVoteType::NoVote) { continue; } @@ -144,10 +144,11 @@ LayerHistoryV2::Summary LayerHistoryV2::summarize(nsecs_t now) { const float layerArea = transformed.getWidth() * transformed.getHeight(); float weight = mDisplayArea ? layerArea / mDisplayArea : 0.0f; - summary.push_back({strong->getName(), type, refreshRate, weight, layerFocused}); + summary.push_back({strong->getName(), vote.type, vote.fps, vote.shouldBeSeamless, weight, + layerFocused}); if (CC_UNLIKELY(mTraceEnabled)) { - trace(layer, *info, type, static_cast<int>(std::round(refreshRate))); + trace(layer, *info, vote.type, static_cast<int>(std::round(vote.fps))); } } @@ -178,7 +179,7 @@ void LayerHistoryV2::partitionLayers(nsecs_t now) { if (frameRate.rate > 0 || voteType == LayerVoteType::NoVote) { const auto type = layer->isVisible() ? voteType : LayerVoteType::NoVote; - info->setLayerVote(type, frameRate.rate); + info->setLayerVote({type, frameRate.rate, frameRate.shouldBeSeamless}); } else { info->resetLayerVote(); } diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp index 44f20d0063..94e7e20251 100644 --- a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp +++ b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp @@ -198,10 +198,10 @@ std::optional<float> LayerInfoV2::calculateRefreshRateIfPossible(nsecs_t now) { : std::make_optional(mLastRefreshRate.reported); } -std::pair<LayerHistory::LayerVoteType, float> LayerInfoV2::getRefreshRate(nsecs_t now) { +LayerInfoV2::LayerVote LayerInfoV2::getRefreshRateVote(nsecs_t now) { if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) { ALOGV("%s voted %d ", mName.c_str(), static_cast<int>(mLayerVote.type)); - return {mLayerVote.type, mLayerVote.fps}; + return mLayerVote; } if (isAnimating(now)) { diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.h b/services/surfaceflinger/Scheduler/LayerInfoV2.h index 33dc66fd19..2305bc3055 100644 --- a/services/surfaceflinger/Scheduler/LayerInfoV2.h +++ b/services/surfaceflinger/Scheduler/LayerInfoV2.h @@ -56,6 +56,13 @@ class LayerInfoV2 { friend class LayerHistoryTestV2; public: + // Holds information about the layer vote + struct LayerVote { + LayerHistory::LayerVoteType type = LayerHistory::LayerVoteType::Heuristic; + float fps = 0.0f; + bool shouldBeSeamless = true; + }; + static void setTraceEnabled(bool enabled) { sTraceEnabled = enabled; } static void setRefreshRateConfigs(const RefreshRateConfigs& refreshRateConfigs) { @@ -76,7 +83,7 @@ public: // Sets an explicit layer vote. This usually comes directly from the application via // ANativeWindow_setFrameRate API - void setLayerVote(LayerHistory::LayerVoteType type, float fps) { mLayerVote = {type, fps}; } + void setLayerVote(LayerVote vote) { mLayerVote = vote; } // Sets the default layer vote. This will be the layer vote after calling to resetLayerVote(). // This is used for layers that called to setLayerVote() and then removed the vote, so that the @@ -84,9 +91,9 @@ public: void setDefaultLayerVote(LayerHistory::LayerVoteType type) { mDefaultVote = type; } // Resets the layer vote to its default. - void resetLayerVote() { mLayerVote = {mDefaultVote, 0.0f}; } + void resetLayerVote() { mLayerVote = {mDefaultVote, 0.0f, true}; } - std::pair<LayerHistory::LayerVoteType, float> getRefreshRate(nsecs_t now); + LayerVote getRefreshRateVote(nsecs_t now); // Return the last updated time. If the present time is farther in the future than the // updated time, the updated time is the present time. @@ -130,12 +137,6 @@ private: bool animatingOrInfrequent = false; }; - // Holds information about the layer vote - struct LayerVote { - LayerHistory::LayerVoteType type = LayerHistory::LayerVoteType::Heuristic; - float fps = 0.0f; - }; - // Class to store past calculated refresh rate and determine whether // the refresh rate calculated is consistent with past values class RefreshRateHistory { diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp index 150f925fe0..0646c7dfd5 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp @@ -31,6 +31,12 @@ namespace android::scheduler { using AllRefreshRatesMapType = RefreshRateConfigs::AllRefreshRatesMapType; using RefreshRate = RefreshRateConfigs::RefreshRate; +std::string RefreshRate::toString() const { + return base::StringPrintf("{id=%d, hwcId=%d, fps=%.2f, width=%d, height=%d group=%d}", + getConfigId().value(), hwcConfig->getId(), getFps(), + hwcConfig->getWidth(), hwcConfig->getHeight(), getConfigGroup()); +} + std::string RefreshRateConfigs::layerVoteTypeString(LayerVoteType vote) { switch (vote) { case LayerVoteType::NoVote: @@ -125,7 +131,7 @@ const RefreshRate& RefreshRateConfigs::getBestRefreshRate( const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals, GlobalSignals* outSignalsConsidered) const { ATRACE_CALL(); - ALOGV("getRefreshRateForContent %zu layers", layers.size()); + ALOGV("getBestRefreshRate %zu layers", layers.size()); if (outSignalsConsidered) *outSignalsConsidered = {}; const auto setTouchConsidered = [&] { @@ -148,6 +154,7 @@ const RefreshRate& RefreshRateConfigs::getBestRefreshRate( int explicitDefaultVoteLayers = 0; int explicitExactOrMultipleVoteLayers = 0; float maxExplicitWeight = 0; + int seamedLayers = 0; for (const auto& layer : layers) { if (layer.vote == LayerVoteType::NoVote) { noVoteLayers++; @@ -162,6 +169,10 @@ const RefreshRate& RefreshRateConfigs::getBestRefreshRate( explicitExactOrMultipleVoteLayers++; maxExplicitWeight = std::max(maxExplicitWeight, layer.weight); } + + if (!layer.shouldBeSeamless) { + seamedLayers++; + } } const bool hasExplicitVoteLayers = @@ -206,6 +217,8 @@ const RefreshRate& RefreshRateConfigs::getBestRefreshRate( scores.emplace_back(refreshRate, 0.0f); } + const auto& defaultConfig = mRefreshRates.at(policy->defaultConfig); + for (const auto& layer : layers) { ALOGV("Calculating score for %s (%s, weight %.2f)", layer.name.c_str(), layerVoteTypeString(layer.vote).c_str(), layer.weight); @@ -216,6 +229,30 @@ const RefreshRate& RefreshRateConfigs::getBestRefreshRate( auto weight = layer.weight; for (auto i = 0u; i < scores.size(); i++) { + // If there are no layers with shouldBeSeamless=false and the current + // config group is different from the default one, this means a layer with + // shouldBeSeamless=false has just disappeared and we should switch back to + // the default config group. + const bool isSeamlessSwitch = seamedLayers > 0 + ? scores[i].first->getConfigGroup() == mCurrentRefreshRate->getConfigGroup() + : scores[i].first->getConfigGroup() == defaultConfig->getConfigGroup(); + + if (layer.shouldBeSeamless && !isSeamlessSwitch) { + ALOGV("%s (weight %.2f) ignores %s (group=%d) to avoid non-seamless switch." + "Current config = %s", + layer.name.c_str(), weight, scores[i].first->name.c_str(), + scores[i].first->getConfigGroup(), mCurrentRefreshRate->toString().c_str()); + continue; + } + + if (!layer.shouldBeSeamless && !isSeamlessSwitch && !layer.focused) { + ALOGV("%s (weight %.2f) ignores %s (group=%d) because it's not focused" + " and the switch is going to be seamed. Current config = %s", + layer.name.c_str(), weight, scores[i].first->name.c_str(), + scores[i].first->getConfigGroup(), mCurrentRefreshRate->toString().c_str()); + continue; + } + bool inPrimaryRange = scores[i].first->inPolicy(policy->primaryRange.min, policy->primaryRange.max); if ((primaryRangeIsSingleRate || !inPrimaryRange) && @@ -292,10 +329,13 @@ const RefreshRate& RefreshRateConfigs::getBestRefreshRate( return 1.0f / iter; }(); + // Slightly prefer seamless switches. + constexpr float kSeamedSwitchPenalty = 0.95f; + const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty; ALOGV("%s (%s, weight %.2f) %.2fHz gives %s score of %.2f", layer.name.c_str(), layerVoteTypeString(layer.vote).c_str(), weight, 1e9f / layerPeriod, scores[i].first->name.c_str(), layerScore); - scores[i].second += weight * layerScore; + scores[i].second += weight * layerScore * seamlessness; continue; } } @@ -367,6 +407,15 @@ const RefreshRate& RefreshRateConfigs::getMinRefreshRateByPolicy() const { } const RefreshRate& RefreshRateConfigs::getMinRefreshRateByPolicyLocked() const { + for (auto refreshRate : mPrimaryRefreshRates) { + if (mCurrentRefreshRate->getConfigGroup() == refreshRate->getConfigGroup()) { + return *refreshRate; + } + } + ALOGE("Can't find min refresh rate by policy with the same config group" + " as the current config %s", + mCurrentRefreshRate->toString().c_str()); + // Defaulting to the lowest refresh rate return *mPrimaryRefreshRates.front(); } @@ -376,6 +425,16 @@ const RefreshRate& RefreshRateConfigs::getMaxRefreshRateByPolicy() const { } const RefreshRate& RefreshRateConfigs::getMaxRefreshRateByPolicyLocked() const { + for (auto it = mPrimaryRefreshRates.rbegin(); it != mPrimaryRefreshRates.rend(); it++) { + const auto& refreshRate = (**it); + if (mCurrentRefreshRate->getConfigGroup() == refreshRate.getConfigGroup()) { + return refreshRate; + } + } + ALOGE("Can't find max refresh rate by policy with the same config group" + " as the current config %s", + mCurrentRefreshRate->toString().c_str()); + // Defaulting to the highest refresh rate return *mPrimaryRefreshRates.back(); } @@ -414,7 +473,7 @@ RefreshRateConfigs::RefreshRateConfigs( const float fps = 1e9f / config->getVsyncPeriod(); mRefreshRates.emplace(configId, std::make_unique<RefreshRate>(configId, config, - base::StringPrintf("%.0ffps", fps), fps, + base::StringPrintf("%.2ffps", fps), fps, RefreshRate::ConstructorTag(0))); if (configId == currentConfigId) { mCurrentRefreshRate = mRefreshRates.at(configId).get(); diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h index 8ff92a095c..41e54a7e7b 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h @@ -86,6 +86,8 @@ public: bool operator==(const RefreshRate& other) const { return !(*this != other); } + std::string toString() const; + private: friend RefreshRateConfigs; friend class RefreshRateConfigsTest; @@ -216,6 +218,8 @@ public: LayerVoteType vote = LayerVoteType::NoVote; // Layer's desired refresh rate, if applicable. float desiredRefreshRate = 0.0f; + // If a seamless mode switch is required. + bool shouldBeSeamless = true; // Layer's weight in the range of [0, 1]. The higher the weight the more impact this layer // would have on choosing the refresh rate. float weight = 0.0f; diff --git a/services/surfaceflinger/Scheduler/StrongTyping.h b/services/surfaceflinger/Scheduler/StrongTyping.h index e8ca0ba836..6a60257c81 100644 --- a/services/surfaceflinger/Scheduler/StrongTyping.h +++ b/services/surfaceflinger/Scheduler/StrongTyping.h @@ -70,6 +70,10 @@ struct StrongTyping : Ability<StrongTyping<T, W, Ability...>>... { T const& value() const { return mValue; } T& value() { return mValue; } + friend std::ostream& operator<<(std::ostream& os, const StrongTyping<T, W, Ability...>& value) { + return os << value.value(); + } + private: T mValue; }; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 57c4d52653..28a8e7ac12 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -3781,7 +3781,8 @@ uint32_t SurfaceFlinger::setClientStateLocked( "SurfaceFlinger::setClientStateLocked") && layer->setFrameRate(Layer::FrameRate(s.frameRate, Layer::FrameRate::convertCompatibility( - s.frameRateCompatibility)))) { + s.frameRateCompatibility), + s.shouldBeSeamless))) { flags |= eTraversalNeeded; } } @@ -6135,7 +6136,7 @@ const std::unordered_map<std::string, uint32_t>& SurfaceFlinger::getGenericLayer } status_t SurfaceFlinger::setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate, - int8_t compatibility) { + int8_t compatibility, bool shouldBeSeamless) { if (!ValidateFrameRate(frameRate, compatibility, "SurfaceFlinger::setFrameRate")) { return BAD_VALUE; } @@ -6148,10 +6149,10 @@ status_t SurfaceFlinger::setFrameRate(const sp<IGraphicBufferProducer>& surface, ALOGE("Attempt to set frame rate on a layer that no longer exists"); return BAD_VALUE; } - if (layer->setFrameRate( Layer::FrameRate(frameRate, - Layer::FrameRate::convertCompatibility(compatibility)))) { + Layer::FrameRate::convertCompatibility(compatibility), + shouldBeSeamless))) { setTransactionFlags(eTraversalNeeded); } } else { diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index a821d4473f..9666f145b0 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -600,7 +600,7 @@ private: status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor, float lightPosY, float lightPosZ, float lightRadius) override; status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate, - int8_t compatibility) override; + int8_t compatibility, bool shouldBeSeamless) override; status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) override; status_t setFrameTimelineVsync(const sp<IGraphicBufferProducer>& surface, diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h index da71dad2d4..b87c734e27 100644 --- a/services/surfaceflinger/tests/LayerTransactionTest.h +++ b/services/surfaceflinger/tests/LayerTransactionTest.h @@ -123,7 +123,7 @@ protected: } virtual void fillBufferQueueLayerColor(const sp<SurfaceControl>& layer, const Color& color, - int32_t bufferWidth, int32_t bufferHeight) { + uint32_t bufferWidth, uint32_t bufferHeight) { ANativeWindow_Buffer buffer; ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer)); TransactionUtils::fillANativeWindowBufferColor(buffer, @@ -145,7 +145,7 @@ protected: } void fillLayerColor(uint32_t mLayerType, const sp<SurfaceControl>& layer, const Color& color, - int32_t bufferWidth, int32_t bufferHeight) { + uint32_t bufferWidth, uint32_t bufferHeight) { switch (mLayerType) { case ISurfaceComposerClient::eFXSurfaceBufferQueue: fillBufferQueueLayerColor(layer, color, bufferWidth, bufferHeight); diff --git a/services/surfaceflinger/tests/SetFrameRate_test.cpp b/services/surfaceflinger/tests/SetFrameRate_test.cpp index 02ba9e290d..d1bed0cc83 100644 --- a/services/surfaceflinger/tests/SetFrameRate_test.cpp +++ b/services/surfaceflinger/tests/SetFrameRate_test.cpp @@ -14,10 +14,6 @@ * limitations under the License. */ -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" - #include <system/window.h> #include <thread> @@ -50,8 +46,8 @@ protected: } } - const int mLayerWidth = 32; - const int mLayerHeight = 32; + const uint32_t mLayerWidth = 32; + const uint32_t mLayerHeight = 32; sp<SurfaceControl> mLayer; uint32_t mLayerType; }; @@ -59,26 +55,27 @@ protected: TEST_F(SetFrameRateTest, BufferQueueLayerSetFrameRate) { CreateLayer(ISurfaceComposerClient::eFXSurfaceBufferQueue); native_window_set_frame_rate(mLayer->getSurface().get(), 100.f, - ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT); + ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, + /* shouldBeSeamless */ true); ASSERT_NO_FATAL_FAILURE(PostBuffers(Color::RED)); Transaction() - .setFrameRate(mLayer, 200.f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT) + .setFrameRate(mLayer, 200.f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, + /* shouldBeSeamless */ true) .apply(); ASSERT_NO_FATAL_FAILURE(PostBuffers(Color::RED)); native_window_set_frame_rate(mLayer->getSurface().get(), 300.f, - ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT); + ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, + /* shouldBeSeamless */ true); ASSERT_NO_FATAL_FAILURE(PostBuffers(Color::RED)); } TEST_F(SetFrameRateTest, BufferStateLayerSetFrameRate) { CreateLayer(ISurfaceComposerClient::eFXSurfaceBufferState); Transaction() - .setFrameRate(mLayer, 400.f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT) + .setFrameRate(mLayer, 400.f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, + /* shouldBeSeamless */ true) .apply(); ASSERT_NO_FATAL_FAILURE(PostBuffers(Color::GREEN)); } } // namespace android - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion"
\ No newline at end of file diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h index 01badf4ad9..a361b1e956 100644 --- a/services/surfaceflinger/tests/TransactionTestHarnesses.h +++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h @@ -98,14 +98,14 @@ public: outTransformHint, format); } - void fillLayerColor(const sp<SurfaceControl>& layer, const Color& color, int32_t bufferWidth, - int32_t bufferHeight) { + void fillLayerColor(const sp<SurfaceControl>& layer, const Color& color, uint32_t bufferWidth, + uint32_t bufferHeight) { ASSERT_NO_FATAL_FAILURE(LayerTransactionTest::fillLayerColor(mLayerType, layer, color, bufferWidth, bufferHeight)); } - void fillLayerQuadrant(const sp<SurfaceControl>& layer, int32_t bufferWidth, - int32_t bufferHeight, const Color& topLeft, const Color& topRight, + void fillLayerQuadrant(const sp<SurfaceControl>& layer, uint32_t bufferWidth, + uint32_t bufferHeight, const Color& topLeft, const Color& topRight, const Color& bottomLeft, const Color& bottomRight) { ASSERT_NO_FATAL_FAILURE(LayerTransactionTest::fillLayerQuadrant(mLayerType, layer, bufferWidth, bufferHeight, diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp index cb376cd7bb..3b50321102 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp @@ -78,7 +78,7 @@ protected: for (auto& [weak, info] : history().mLayerInfos) { if (auto strong = weak.promote(); strong && strong.get() == layer) { info->setDefaultLayerVote(vote); - info->setLayerVote(vote, 0); + info->setLayerVote({vote, 0, false}); return; } } diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp index 4762fd4b66..df7611043a 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp @@ -57,6 +57,8 @@ protected: static inline const HwcConfigIndexType HWC_CONFIG_ID_72 = HwcConfigIndexType(2); static inline const HwcConfigIndexType HWC_CONFIG_ID_120 = HwcConfigIndexType(3); static inline const HwcConfigIndexType HWC_CONFIG_ID_30 = HwcConfigIndexType(4); + static inline const HwcConfigIndexType HWC_CONFIG_ID_25 = HwcConfigIndexType(5); + static inline const HwcConfigIndexType HWC_CONFIG_ID_50 = HwcConfigIndexType(6); // Test configs std::shared_ptr<const HWC2::Display::Config> mConfig60 = @@ -77,8 +79,16 @@ protected: createConfig(HWC_CONFIG_ID_120, 1, static_cast<int64_t>(1e9f / 120)); std::shared_ptr<const HWC2::Display::Config> mConfig30 = createConfig(HWC_CONFIG_ID_30, 0, static_cast<int64_t>(1e9f / 30)); + std::shared_ptr<const HWC2::Display::Config> mConfig30DifferentGroup = + createConfig(HWC_CONFIG_ID_30, 1, static_cast<int64_t>(1e9f / 30)); + std::shared_ptr<const HWC2::Display::Config> mConfig25DifferentGroup = + createConfig(HWC_CONFIG_ID_25, 1, static_cast<int64_t>(1e9f / 25)); + std::shared_ptr<const HWC2::Display::Config> mConfig50 = + createConfig(HWC_CONFIG_ID_50, 0, static_cast<int64_t>(1e9f / 50)); // Test device configurations + // The positions of the configs in the arrays below MUST match their IDs. For example, + // the first config should always be 60Hz, the second 90Hz etc. std::vector<std::shared_ptr<const HWC2::Display::Config>> m60OnlyConfigDevice = {mConfig60}; std::vector<std::shared_ptr<const HWC2::Display::Config>> m60_90Device = {mConfig60, mConfig90}; std::vector<std::shared_ptr<const HWC2::Display::Config>> m60_90DeviceWithDifferentGroups = @@ -104,6 +114,14 @@ protected: {mConfig60, mConfig90, mConfig72, mConfig120DifferentGroup, mConfig30}; std::vector<std::shared_ptr<const HWC2::Display::Config>> m30_60_90Device = {mConfig60, mConfig90, mConfig72DifferentGroup, mConfig120DifferentGroup, mConfig30}; + std::vector<std::shared_ptr<const HWC2::Display::Config>> m25_30_50_60Device = + {mConfig60, + mConfig90, + mConfig72DifferentGroup, + mConfig120DifferentGroup, + mConfig30DifferentGroup, + mConfig25DifferentGroup, + mConfig50}; // Expected RefreshRate objects RefreshRate mExpected60Config = {HWC_CONFIG_ID_60, mConfig60, "60fps", 60, @@ -292,8 +310,8 @@ TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContent) { /*currentConfigId=*/HWC_CONFIG_ID_60); const auto makeLayerRequirements = [](float refreshRate) -> std::vector<LayerRequirement> { - return {{"testLayer", LayerVoteType::Heuristic, refreshRate, /*weight*/ 1.0f, - /*focused*/ false}}; + return {{"testLayer", LayerVoteType::Heuristic, refreshRate, /*shouldBeSeamless*/ true, + /*weight*/ 1.0f, /*focused*/ false}}; }; EXPECT_EQ(mExpected90Config, @@ -1245,7 +1263,9 @@ TEST_F(RefreshRateConfigsTest, groupSwitching) { auto& layer = layers[0]; layer.vote = LayerVoteType::ExplicitDefault; layer.desiredRefreshRate = 90.0f; + layer.shouldBeSeamless = false; layer.name = "90Hz ExplicitDefault"; + layer.focused = true; ASSERT_EQ(HWC_CONFIG_ID_60, refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}) @@ -1258,6 +1278,104 @@ TEST_F(RefreshRateConfigsTest, groupSwitching) { ASSERT_EQ(HWC_CONFIG_ID_90, refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}) .getConfigId()); + + // Verify that we won't change the group if seamless switch is required. + layer.shouldBeSeamless = true; + ASSERT_EQ(HWC_CONFIG_ID_60, + refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}) + .getConfigId()); + + // At this point the default config in the DisplayManager policy with be 60Hz. + // Verify that if the current config is in another group and there are no layers with + // shouldBeSeamless=false we'll go back to the default group. + refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90); + layer.desiredRefreshRate = 60.0f; + layer.name = "60Hz ExplicitDefault"; + layer.shouldBeSeamless = true; + ASSERT_EQ(HWC_CONFIG_ID_60, + refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}) + .getConfigId()); + + // If there's a layer with shouldBeSeamless=false, another layer with shouldBeSeamless=true + // can't change the config group. + refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90); + auto layer2 = LayerRequirement{.weight = 0.5f}; + layer2.vote = LayerVoteType::ExplicitDefault; + layer2.desiredRefreshRate = 90.0f; + layer2.name = "90Hz ExplicitDefault"; + layer2.shouldBeSeamless = false; + layer2.focused = false; + layers.push_back(layer2); + ASSERT_EQ(HWC_CONFIG_ID_90, + refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}) + .getConfigId()); +} + +TEST_F(RefreshRateConfigsTest, nonSeamlessVotePrefersSeamlessSwitches) { + auto refreshRateConfigs = + std::make_unique<RefreshRateConfigs>(m30_60Device, + /*currentConfigId=*/HWC_CONFIG_ID_60); + + // Allow group switching. + RefreshRateConfigs::Policy policy; + policy.defaultConfig = refreshRateConfigs->getCurrentPolicy().defaultConfig; + policy.allowGroupSwitching = true; + ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0); + + auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}}; + auto& layer = layers[0]; + layer.vote = LayerVoteType::ExplicitExactOrMultiple; + layer.desiredRefreshRate = 60.0f; + layer.shouldBeSeamless = false; + layer.name = "60Hz ExplicitExactOrMultiple"; + layer.focused = true; + + ASSERT_EQ(HWC_CONFIG_ID_60, + refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}) + .getConfigId()); + + refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_120); + ASSERT_EQ(HWC_CONFIG_ID_120, + refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}) + .getConfigId()); +} + +TEST_F(RefreshRateConfigsTest, nonSeamlessExactAndSeamlessMultipleLayers) { + auto refreshRateConfigs = + std::make_unique<RefreshRateConfigs>(m25_30_50_60Device, + /*currentConfigId=*/HWC_CONFIG_ID_60); + + // Allow group switching. + RefreshRateConfigs::Policy policy; + policy.defaultConfig = refreshRateConfigs->getCurrentPolicy().defaultConfig; + policy.allowGroupSwitching = true; + ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0); + + auto layers = std::vector< + LayerRequirement>{LayerRequirement{.name = "60Hz ExplicitDefault", + .vote = LayerVoteType::ExplicitDefault, + .desiredRefreshRate = 60.0f, + .shouldBeSeamless = false, + .weight = 0.5f, + .focused = false}, + LayerRequirement{.name = "25Hz ExplicitExactOrMultiple", + .vote = LayerVoteType::ExplicitExactOrMultiple, + .desiredRefreshRate = 25.0f, + .shouldBeSeamless = true, + .weight = 1.0f, + .focused = true}}; + auto& seamedLayer = layers[0]; + + ASSERT_EQ(HWC_CONFIG_ID_50, + refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}) + .getConfigId()); + + seamedLayer.name = "30Hz ExplicitDefault", seamedLayer.desiredRefreshRate = 30.0f; + refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_30); + + ASSERT_EQ(HWC_CONFIG_ID_25, + refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}) + .getConfigId()); } TEST_F(RefreshRateConfigsTest, primaryVsAppRequestPolicy) { diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp index de66f8fb41..d0bb9e291a 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp @@ -108,7 +108,7 @@ TEST_F(RefreshRateStatsTest, oneConfigTest) { std::this_thread::sleep_for(std::chrono::milliseconds(2)); times = mRefreshRateStats->getTotalTimes(); EXPECT_LT(screenOff, times["ScreenOff"]); - EXPECT_EQ(0u, times.count("90fps")); + EXPECT_EQ(0u, times.count("90.00fps")); mRefreshRateStats->setConfigMode(CONFIG_ID_0); mRefreshRateStats->setPowerMode(PowerMode::ON); @@ -116,15 +116,15 @@ TEST_F(RefreshRateStatsTest, oneConfigTest) { std::this_thread::sleep_for(std::chrono::milliseconds(2)); times = mRefreshRateStats->getTotalTimes(); EXPECT_EQ(screenOff, times["ScreenOff"]); - ASSERT_EQ(1u, times.count("90fps")); - EXPECT_LT(0, times["90fps"]); + ASSERT_EQ(1u, times.count("90.00fps")); + EXPECT_LT(0, times["90.00fps"]); mRefreshRateStats->setPowerMode(PowerMode::DOZE); - int ninety = mRefreshRateStats->getTotalTimes()["90fps"]; + int ninety = mRefreshRateStats->getTotalTimes()["90.00fps"]; std::this_thread::sleep_for(std::chrono::milliseconds(2)); times = mRefreshRateStats->getTotalTimes(); EXPECT_LT(screenOff, times["ScreenOff"]); - EXPECT_EQ(ninety, times["90fps"]); + EXPECT_EQ(ninety, times["90.00fps"]); mRefreshRateStats->setConfigMode(CONFIG_ID_0); screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"]; @@ -133,7 +133,7 @@ TEST_F(RefreshRateStatsTest, oneConfigTest) { // Because the power mode is not PowerMode::ON, switching the config // does not update refresh rates that come from the config. EXPECT_LT(screenOff, times["ScreenOff"]); - EXPECT_EQ(ninety, times["90fps"]); + EXPECT_EQ(ninety, times["90.00fps"]); } TEST_F(RefreshRateStatsTest, twoConfigsTest) { @@ -163,53 +163,53 @@ TEST_F(RefreshRateStatsTest, twoConfigsTest) { std::this_thread::sleep_for(std::chrono::milliseconds(2)); times = mRefreshRateStats->getTotalTimes(); EXPECT_EQ(screenOff, times["ScreenOff"]); - ASSERT_EQ(1u, times.count("90fps")); - EXPECT_LT(0, times["90fps"]); + ASSERT_EQ(1u, times.count("90.00fps")); + EXPECT_LT(0, times["90.00fps"]); // When power mode is normal, time for configs updates. mRefreshRateStats->setConfigMode(CONFIG_ID_1); - int ninety = mRefreshRateStats->getTotalTimes()["90fps"]; + int ninety = mRefreshRateStats->getTotalTimes()["90.00fps"]; std::this_thread::sleep_for(std::chrono::milliseconds(2)); times = mRefreshRateStats->getTotalTimes(); EXPECT_EQ(screenOff, times["ScreenOff"]); - EXPECT_EQ(ninety, times["90fps"]); - ASSERT_EQ(1u, times.count("60fps")); - EXPECT_LT(0, times["60fps"]); + EXPECT_EQ(ninety, times["90.00fps"]); + ASSERT_EQ(1u, times.count("60.00fps")); + EXPECT_LT(0, times["60.00fps"]); mRefreshRateStats->setConfigMode(CONFIG_ID_0); - int sixty = mRefreshRateStats->getTotalTimes()["60fps"]; + int sixty = mRefreshRateStats->getTotalTimes()["60.00fps"]; std::this_thread::sleep_for(std::chrono::milliseconds(2)); times = mRefreshRateStats->getTotalTimes(); EXPECT_EQ(screenOff, times["ScreenOff"]); - EXPECT_LT(ninety, times["90fps"]); - EXPECT_EQ(sixty, times["60fps"]); + EXPECT_LT(ninety, times["90.00fps"]); + EXPECT_EQ(sixty, times["60.00fps"]); mRefreshRateStats->setConfigMode(CONFIG_ID_1); - ninety = mRefreshRateStats->getTotalTimes()["90fps"]; + ninety = mRefreshRateStats->getTotalTimes()["90.00fps"]; std::this_thread::sleep_for(std::chrono::milliseconds(2)); times = mRefreshRateStats->getTotalTimes(); EXPECT_EQ(screenOff, times["ScreenOff"]); - EXPECT_EQ(ninety, times["90fps"]); - EXPECT_LT(sixty, times["60fps"]); + EXPECT_EQ(ninety, times["90.00fps"]); + EXPECT_LT(sixty, times["60.00fps"]); // Because the power mode is not PowerMode::ON, switching the config // does not update refresh rates that come from the config. mRefreshRateStats->setPowerMode(PowerMode::DOZE); mRefreshRateStats->setConfigMode(CONFIG_ID_0); - sixty = mRefreshRateStats->getTotalTimes()["60fps"]; + sixty = mRefreshRateStats->getTotalTimes()["60.00fps"]; std::this_thread::sleep_for(std::chrono::milliseconds(2)); times = mRefreshRateStats->getTotalTimes(); EXPECT_LT(screenOff, times["ScreenOff"]); - EXPECT_EQ(ninety, times["90fps"]); - EXPECT_EQ(sixty, times["60fps"]); + EXPECT_EQ(ninety, times["90.00fps"]); + EXPECT_EQ(sixty, times["60.00fps"]); mRefreshRateStats->setConfigMode(CONFIG_ID_1); screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"]; std::this_thread::sleep_for(std::chrono::milliseconds(2)); times = mRefreshRateStats->getTotalTimes(); EXPECT_LT(screenOff, times["ScreenOff"]); - EXPECT_EQ(ninety, times["90fps"]); - EXPECT_EQ(sixty, times["60fps"]); + EXPECT_EQ(ninety, times["90.00fps"]); + EXPECT_EQ(sixty, times["60.00fps"]); } } // namespace } // namespace scheduler |