SurfaceFlinger: Force HDR content on DEFAULT refresh rate

Do not allow Performance Refresh Rate when displaying HDR content.

Test: HDR Video on YouTube
Bug: 129694529
Change-Id: Ic9b5801d3a4c8b06964e0c4dcec95ef214ebedc6
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 8e36ae9..1db43a3 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -67,7 +67,8 @@
     }
 }
 
-void LayerHistory::insert(const std::unique_ptr<LayerHandle>& layerHandle, nsecs_t presentTime) {
+void LayerHistory::insert(const std::unique_ptr<LayerHandle>& layerHandle, nsecs_t presentTime,
+                          bool isHdr) {
     std::shared_ptr<LayerInfo> layerInfo;
     {
         std::lock_guard lock(mLock);
@@ -88,9 +89,36 @@
         }
     }
     layerInfo->setLastPresentTime(presentTime);
+    layerInfo->setHDRContent(isHdr);
 }
 
-float LayerHistory::getDesiredRefreshRate() {
+void LayerHistory::setVisibility(const std::unique_ptr<LayerHandle>& layerHandle, bool visible) {
+    std::shared_ptr<LayerInfo> layerInfo;
+    {
+        std::lock_guard lock(mLock);
+        auto layerInfoIterator = mInactiveLayerInfos.find(layerHandle->mId);
+        if (layerInfoIterator != mInactiveLayerInfos.end()) {
+            layerInfo = layerInfoIterator->second;
+            if (visible) {
+                mInactiveLayerInfos.erase(layerInfoIterator);
+                mActiveLayerInfos.insert({layerHandle->mId, layerInfo});
+            }
+        } else {
+            layerInfoIterator = mActiveLayerInfos.find(layerHandle->mId);
+            if (layerInfoIterator != mActiveLayerInfos.end()) {
+                layerInfo = layerInfoIterator->second;
+            } else {
+                ALOGW("Inserting information about layer that is not registered: %" PRId64,
+                      layerHandle->mId);
+                return;
+            }
+        }
+    }
+    layerInfo->setVisibility(visible);
+}
+
+std::pair<float, bool> LayerHistory::getDesiredRefreshRateAndHDR() {
+    bool isHDR = false;
     float newRefreshRate = 0.f;
     std::lock_guard lock(mLock);
 
@@ -108,12 +136,13 @@
         if (layerInfo->isRecentlyActive() && layerRefreshRate > newRefreshRate) {
             newRefreshRate = layerRefreshRate;
         }
+        isHDR |= layerInfo->getHDRContent();
     }
     if (mTraceEnabled) {
         ALOGD("LayerHistory DesiredRefreshRate: %.2f", newRefreshRate);
     }
 
-    return newRefreshRate;
+    return {newRefreshRate, isHDR};
 }
 
 void LayerHistory::removeIrrelevantLayers() {
@@ -122,7 +151,9 @@
     auto it = mActiveLayerInfos.begin();
     while (it != mActiveLayerInfos.end()) {
         // If last updated was before the obsolete time, remove it.
-        if (it->second->getLastUpdatedTime() < obsoleteEpsilon) {
+        // Keep HDR layer around as long as they are visible.
+        if (!it->second->isVisible() ||
+            (!it->second->getHDRContent() && it->second->getLastUpdatedTime() < obsoleteEpsilon)) {
             // erase() function returns the iterator of the next
             // to last deleted element.
             if (mTraceEnabled) {
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 39061e7..adc5ce5 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -56,10 +56,13 @@
     std::unique_ptr<LayerHandle> createLayer(const std::string name, float maxRefreshRate);
 
     // Method for inserting layers and their requested present time into the unordered map.
-    void insert(const std::unique_ptr<LayerHandle>& layerHandle, nsecs_t presentTime);
+    void insert(const std::unique_ptr<LayerHandle>& layerHandle, nsecs_t presentTime, bool isHdr);
+    // Method for setting layer visibility
+    void setVisibility(const std::unique_ptr<LayerHandle>& layerHandle, bool visible);
+
     // Returns the desired refresh rate, which is a max refresh rate of all the current
     // layers. See go/content-fps-detection-in-scheduler for more information.
-    float getDesiredRefreshRate();
+    std::pair<float, bool> getDesiredRefreshRateAndHDR();
 
     // Removes the handle and the object from the map.
     void destroyLayer(const int64_t id);
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 1970a47..02b6aef 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -115,6 +115,16 @@
     // updated time, the updated time is the present time.
     void setLastPresentTime(nsecs_t lastPresentTime);
 
+    void setHDRContent(bool isHdr) {
+        std::lock_guard lock(mLock);
+        mIsHDR = isHdr;
+    }
+
+    void setVisibility(bool visible) {
+        std::lock_guard lock(mLock);
+        mIsVisible = visible;
+    }
+
     // Checks the present time history to see whether the layer is relevant.
     bool isRecentlyActive() const {
         std::lock_guard lock(mLock);
@@ -127,6 +137,16 @@
         return mRefreshRateHistory.getRefreshRateAvg();
     }
 
+    bool getHDRContent() {
+        std::lock_guard lock(mLock);
+        return mIsHDR;
+    }
+
+    bool isVisible() {
+        std::lock_guard lock(mLock);
+        return mIsVisible;
+    }
+
     // 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.
     nsecs_t getLastUpdatedTime() {
@@ -150,6 +170,8 @@
     nsecs_t mLastPresentTime GUARDED_BY(mLock) = 0;
     RefreshRateHistory mRefreshRateHistory GUARDED_BY(mLock);
     PresentTimeHistory mPresentTimeHistory GUARDED_BY(mLock);
+    bool mIsHDR GUARDED_BY(mLock) = false;
+    bool mIsVisible GUARDED_BY(mLock) = false;
 };
 
 } // namespace scheduler
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 88d2638..5b87a3d 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -306,10 +306,15 @@
     return mLayerHistory.createLayer(name, fps);
 }
 
-void Scheduler::addLayerPresentTime(
+void Scheduler::addLayerPresentTimeAndHDR(
         const std::unique_ptr<scheduler::LayerHistory::LayerHandle>& layerHandle,
-        nsecs_t presentTime) {
-    mLayerHistory.insert(layerHandle, presentTime);
+        nsecs_t presentTime, bool isHDR) {
+    mLayerHistory.insert(layerHandle, presentTime, isHDR);
+}
+
+void Scheduler::setLayerVisibility(
+        const std::unique_ptr<scheduler::LayerHistory::LayerHandle>& layerHandle, bool visible) {
+    mLayerHistory.setVisibility(layerHandle, visible);
 }
 
 void Scheduler::withPrimaryDispSync(std::function<void(DispSync&)> const& fn) {
@@ -317,18 +322,23 @@
 }
 
 void Scheduler::updateFpsBasedOnContent() {
-    uint32_t refreshRate = std::round(mLayerHistory.getDesiredRefreshRate());
+    auto [refreshRate, isHDR] = mLayerHistory.getDesiredRefreshRateAndHDR();
+    const uint32_t refreshRateRound = std::round(refreshRate);
     RefreshRateType newRefreshRateType;
     {
         std::lock_guard<std::mutex> lock(mFeatureStateLock);
-        if (mContentRefreshRate == refreshRate) {
+        if (mContentRefreshRate == refreshRateRound && mIsHDRContent == isHDR) {
             return;
         }
-        mContentRefreshRate = refreshRate;
+        mContentRefreshRate = refreshRateRound;
         ATRACE_INT("ContentFPS", mContentRefreshRate);
 
-        mCurrentContentFeatureState = refreshRate > 0 ? ContentFeatureState::CONTENT_DETECTION_ON
-                                                      : ContentFeatureState::CONTENT_DETECTION_OFF;
+        mIsHDRContent = isHDR;
+        ATRACE_INT("ContentHDR", mIsHDRContent);
+
+        mCurrentContentFeatureState = refreshRateRound > 0
+                ? ContentFeatureState::CONTENT_DETECTION_ON
+                : ContentFeatureState::CONTENT_DETECTION_OFF;
         newRefreshRateType = calculateRefreshRateType();
         if (mRefreshRateType == newRefreshRateType) {
             return;
@@ -400,6 +410,11 @@
         return RefreshRateType::DEFAULT;
     }
 
+    // HDR content is not supported on PERFORMANCE mode
+    if (mForceHDRContentToDefaultRefreshRate && mIsHDRContent) {
+        return RefreshRateType::DEFAULT;
+    }
+
     // If content detection is off we choose performance as we don't know the content fps
     if (mCurrentContentFeatureState == ContentFeatureState::CONTENT_DETECTION_OFF) {
         return RefreshRateType::PERFORMANCE;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 34327b5..76b1ee3 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -150,9 +150,12 @@
                                                                         int windowType);
 
     // Stores present time for a layer.
-    void addLayerPresentTime(
+    void addLayerPresentTimeAndHDR(
             const std::unique_ptr<scheduler::LayerHistory::LayerHandle>& layerHandle,
-            nsecs_t presentTime);
+            nsecs_t presentTime, bool isHDR);
+    // Stores visibility for a layer.
+    void setLayerVisibility(
+            const std::unique_ptr<scheduler::LayerHistory::LayerHandle>& layerHandle, bool visible);
     // Updates FPS based on the most content presented.
     void updateFpsBasedOnContent();
     // Callback that gets invoked when Scheduler wants to change the refresh rate.
@@ -254,8 +257,12 @@
     IdleTimerState mCurrentIdleTimerState GUARDED_BY(mFeatureStateLock) = IdleTimerState::RESET;
     uint32_t mContentRefreshRate GUARDED_BY(mFeatureStateLock);
     RefreshRateType mRefreshRateType GUARDED_BY(mFeatureStateLock);
+    bool mIsHDRContent GUARDED_BY(mFeatureStateLock) = false;
 
     const scheduler::RefreshRateConfigs& mRefreshRateConfigs;
+
+    // Global config to force HDR content to work on DEFAULT refreshRate
+    static constexpr bool mForceHDRContentToDefaultRefreshRate = true;
 };
 
 } // namespace android