Change small area detection to support new front end design

This will still use legacy layer to calculate the small dirty, and
update the info to the LayerSnapshot.

Also fix some issues in setIsSmallDirty() for
1. Set mSmallDirty flag as false if the surface damage region is
   invalid.
2. Apply the scaling to the damage region before dirty area
   calculations.

Bug: 295062543
Bug: 303258910
Bug: 303609027
Test: atest LayerHistoryTest
Test: atest SmallAreaDetectionTest
Change-Id: Ib0e3737e63b5653422b5b5054b893578dc63f768
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
index 80a51ea..1b6fdae 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
@@ -122,6 +122,8 @@
         ReachableByRelativeParent
     };
     Reachablilty reachablilty;
+    // True when the surfaceDamage is recognized as a small area update.
+    bool isSmallDirty = false;
 
     static bool isOpaqueFormat(PixelFormat format);
     static bool isTransformValid(const ui::Transform& t);
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 73b1ca8..f75f9f6 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -3369,7 +3369,7 @@
     mDrawingState.surfaceDamageRegion = surfaceDamage;
     mDrawingState.modified = true;
     setTransactionFlags(eTransactionNeeded);
-    setIsSmallDirty();
+    setIsSmallDirty(surfaceDamage, getTransform());
     return true;
 }
 
@@ -4416,7 +4416,9 @@
     mLastLatchTime = latchTime;
 }
 
-void Layer::setIsSmallDirty() {
+void Layer::setIsSmallDirty(const Region& damageRegion,
+                            const ui::Transform& layerToDisplayTransform) {
+    mSmallDirty = false;
     if (!mFlinger->mScheduler->supportSmallDirtyDetection()) {
         return;
     }
@@ -4425,17 +4427,26 @@
         mWindowType != WindowInfo::Type::BASE_APPLICATION) {
         return;
     }
-    Rect bounds = mDrawingState.surfaceDamageRegion.getBounds();
+
+    Rect bounds = damageRegion.getBounds();
     if (!bounds.isValid()) {
         return;
     }
 
+    // Transform to screen space.
+    bounds = layerToDisplayTransform.transform(bounds);
+
     // If the damage region is a small dirty, this could give the hint for the layer history that
     // it could suppress the heuristic rate when calculating.
     mSmallDirty = mFlinger->mScheduler->isSmallDirtyArea(mOwnerAppId,
                                                          bounds.getWidth() * bounds.getHeight());
 }
 
+void Layer::setIsSmallDirty(frontend::LayerSnapshot* snapshot) {
+    setIsSmallDirty(snapshot->surfaceDamage, snapshot->localTransform);
+    snapshot->isSmallDirty = mSmallDirty;
+}
+
 } // namespace android
 
 #if defined(__gl_h_)
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index f67da2a..c042395 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -937,7 +937,8 @@
     const sp<SurfaceFlinger> mFlinger;
 
     // Check if the damage region is a small dirty.
-    void setIsSmallDirty();
+    void setIsSmallDirty(const Region& damageRegion, const ui::Transform& layerToDisplayTransform);
+    void setIsSmallDirty(frontend::LayerSnapshot* snapshot);
 
 protected:
     // For unit tests
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index b13c0e8..181a9bf 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2211,8 +2211,29 @@
             continue;
         }
 
-        if (!snapshot->changes.any(Changes::FrameRate | Changes::Buffer | Changes::Animation) &&
-            (snapshot->clientChanges & layer_state_t::eDefaultFrameRateCompatibilityChanged) == 0) {
+        const bool updateSmallDirty = mScheduler->supportSmallDirtyDetection() &&
+                ((snapshot->clientChanges & layer_state_t::eSurfaceDamageRegionChanged) ||
+                 snapshot->changes.any(Changes::Geometry));
+
+        const bool hasChanges =
+                snapshot->changes.any(Changes::FrameRate | Changes::Buffer | Changes::Animation) ||
+                (snapshot->clientChanges & layer_state_t::eDefaultFrameRateCompatibilityChanged) !=
+                        0;
+
+        if (!updateSmallDirty && !hasChanges) {
+            continue;
+        }
+
+        auto it = mLegacyLayers.find(snapshot->sequence);
+        LOG_ALWAYS_FATAL_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;
         }
 
@@ -2222,12 +2243,9 @@
                 .transform = snapshot->geomLayerTransform,
                 .setFrameRateVote = snapshot->frameRate,
                 .frameRateSelectionPriority = snapshot->frameRateSelectionPriority,
+                .isSmallDirty = snapshot->isSmallDirty,
         };
 
-        auto it = mLegacyLayers.find(snapshot->sequence);
-        LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), "Couldnt find layer object for %s",
-                            snapshot->getDebugString().c_str());
-
         if (snapshot->clientChanges & layer_state_t::eDefaultFrameRateCompatibilityChanged) {
             mScheduler->setDefaultFrameRateCompatibility(snapshot->sequence,
                                                          snapshot->defaultFrameRateCompatibility);
@@ -8492,15 +8510,6 @@
 void SurfaceFlinger::onActiveDisplaySizeChanged(const DisplayDevice& activeDisplay) {
     mScheduler->onActiveDisplayAreaChanged(activeDisplay.getWidth() * activeDisplay.getHeight());
     getRenderEngine().onActiveDisplaySizeChanged(activeDisplay.getSize());
-
-    // Notify layers to update small dirty flag.
-    if (mScheduler->supportSmallDirtyDetection()) {
-        mCurrentState.traverse([&](Layer* layer) {
-            if (layer->getLayerStack() == activeDisplay.getLayerStack()) {
-                layer->setIsSmallDirty();
-            }
-        });
-    }
 }
 
 sp<DisplayDevice> SurfaceFlinger::getActivatableDisplay() const {
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
index 7f3171f..48f8923 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
@@ -421,6 +421,17 @@
         mLifecycleManager.applyTransactions(transactions);
     }
 
+    void setDamageRegion(uint32_t id, const Region& damageRegion) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eSurfaceDamageRegionChanged;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().state.surfaceDamageRegion = damageRegion;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
     void setDataspace(uint32_t id, ui::Dataspace dataspace) {
         std::vector<TransactionState> transactions;
         transactions.emplace_back();
@@ -432,6 +443,19 @@
         mLifecycleManager.applyTransactions(transactions);
     }
 
+    void setMatrix(uint32_t id, float dsdx, float dtdx, float dtdy, float dsdy) {
+        layer_state_t::matrix22_t matrix{dsdx, dtdx, dtdy, dsdy};
+
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eMatrixChanged;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().state.matrix = matrix;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
     void setShadowRadius(uint32_t id, float shadowRadius) {
         std::vector<TransactionState> transactions;
         transactions.emplace_back();
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
index a462082..631adf1 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
@@ -866,6 +866,104 @@
     }
 }
 
+class SmallAreaDetectionTest : public LayerHistoryIntegrationTest {
+protected:
+    static constexpr int32_t DISPLAY_WIDTH = 100;
+    static constexpr int32_t DISPLAY_HEIGHT = 100;
+
+    static constexpr int32_t kAppId1 = 10100;
+    static constexpr int32_t kAppId2 = 10101;
+
+    static constexpr float kThreshold1 = 0.05f;
+    static constexpr float kThreshold2 = 0.07f;
+
+    SmallAreaDetectionTest() : LayerHistoryIntegrationTest() {
+        std::vector<std::pair<int32_t, float>> mappings;
+        mappings.reserve(2);
+        mappings.push_back(std::make_pair(kAppId1, kThreshold1));
+        mappings.push_back(std::make_pair(kAppId2, kThreshold2));
+
+        mFlinger.enableNewFrontEnd();
+
+        mScheduler->onActiveDisplayAreaChanged(DISPLAY_WIDTH * DISPLAY_HEIGHT);
+        mScheduler->updateSmallAreaDetection(mappings);
+    }
+
+    auto createLegacyAndFrontedEndLayer(uint32_t sequence) {
+        std::string layerName = "test layer:" + std::to_string(sequence);
+
+        LayerCreationArgs args = LayerCreationArgs{mFlinger.flinger(),
+                                                   nullptr,
+                                                   layerName,
+                                                   0,
+                                                   {},
+                                                   std::make_optional<uint32_t>(sequence)};
+        args.ownerUid = kAppId1;
+        args.metadata.setInt32(gui::METADATA_WINDOW_TYPE, 2); // APPLICATION
+        const auto layer = sp<Layer>::make(args);
+        mFlinger.injectLegacyLayer(layer);
+        createRootLayer(sequence);
+        return layer;
+    }
+};
+
+TEST_F(SmallAreaDetectionTest, SmallDirtyLayer) {
+    auto layer = createLegacyAndFrontedEndLayer(1);
+
+    nsecs_t time = systemTime();
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+
+    uint32_t sequence = static_cast<uint32_t>(layer->sequence);
+    setBuffer(sequence);
+    setDamageRegion(sequence, Region(Rect(10, 10)));
+    updateLayerSnapshotsAndLayerHistory(time);
+
+    ASSERT_EQ(true, mFlinger.mutableLayerSnapshotBuilder().getSnapshot(1)->isSmallDirty);
+}
+
+TEST_F(SmallAreaDetectionTest, NotSmallDirtyLayer) {
+    auto layer = createLegacyAndFrontedEndLayer(1);
+
+    nsecs_t time = systemTime();
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+
+    uint32_t sequence = static_cast<uint32_t>(layer->sequence);
+    setBuffer(sequence);
+    setDamageRegion(sequence, Region(Rect(50, 50)));
+    updateLayerSnapshotsAndLayerHistory(time);
+
+    ASSERT_EQ(false, mFlinger.mutableLayerSnapshotBuilder().getSnapshot(1)->isSmallDirty);
+}
+
+TEST_F(SmallAreaDetectionTest, smallDirtyLayerWithMatrix) {
+    auto layer = createLegacyAndFrontedEndLayer(1);
+
+    nsecs_t time = systemTime();
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+
+    // Original damage region is a small dirty.
+    uint32_t sequence = static_cast<uint32_t>(layer->sequence);
+    setBuffer(sequence);
+    setDamageRegion(sequence, Region(Rect(20, 20)));
+    updateLayerSnapshotsAndLayerHistory(time);
+    ASSERT_EQ(true, mFlinger.mutableLayerSnapshotBuilder().getSnapshot(1)->isSmallDirty);
+
+    setMatrix(sequence, 2.0f, 0, 0, 2.0f);
+    updateLayerSnapshotsAndLayerHistory(time);
+
+    // Verify if the small dirty is scaled.
+    ASSERT_EQ(false, mFlinger.mutableLayerSnapshotBuilder().getSnapshot(1)->isSmallDirty);
+}
+
 INSTANTIATE_TEST_CASE_P(LeapYearTests, LayerHistoryIntegrationTestParameterized,
                         ::testing::Values(1s, 2s, 3s, 4s, 5s));
 
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 014d07c..3d1c900 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -44,7 +44,10 @@
     TestableScheduler(std::unique_ptr<VsyncController> controller,
                       std::shared_ptr<VSyncTracker> tracker, RefreshRateSelectorPtr selectorPtr,
                       sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback)
-          : Scheduler(*this, callback, Feature::kContentDetection, std::move(modulatorPtr)) {
+          : Scheduler(*this, callback,
+                      (FeatureFlags)Feature::kContentDetection |
+                              Feature::kSmallDirtyContentDetection,
+                      std::move(modulatorPtr)) {
         const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId();
         registerDisplay(displayId, std::move(selectorPtr), std::move(controller),
                         std::move(tracker));
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 3b39420..8f1982d 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -670,6 +670,11 @@
         return mFlinger->initTransactionTraceWriter();
     }
 
+    void enableNewFrontEnd() {
+        mFlinger->mLayerLifecycleManagerEnabled = true;
+        mFlinger->mLegacyFrontEndEnabled = false;
+    }
+
     ~TestableSurfaceFlinger() {
         // All these pointer and container clears help ensure that GMock does
         // not report a leaked object, since the SurfaceFlinger instance may