drm_hwcomposer: Allow skipping round-trip to SF before presenting

PresentOrValidateDisplay feature allows to present composition without
a round trip to Surface Flinger in case the client layer does not have
to be updated. This saves some CPU time.

Change-Id: I7969f90a45525b2d9dfdc12a0006cd9b36ac98ba
Signed-off-by: Roman Stratiienko <r.stratiienko@gmail.com>
diff --git a/hwc2_device/HwcDisplay.cpp b/hwc2_device/HwcDisplay.cpp
index 26a60be..121f04f 100644
--- a/hwc2_device/HwcDisplay.cpp
+++ b/hwc2_device/HwcDisplay.cpp
@@ -1279,4 +1279,12 @@
   backend_ = std::move(backend);
 }
 
+bool HwcDisplay::NeedsClientLayerUpdate() const {
+  return std::any_of(layers_.begin(), layers_.end(), [](const auto &pair) {
+    const auto &layer = pair.second;
+    return layer.GetSfType() == HWC2::Composition::Client ||
+           layer.GetValidatedType() == HWC2::Composition::Client;
+  });
+}
+
 }  // namespace android
diff --git a/hwc2_device/HwcDisplay.h b/hwc2_device/HwcDisplay.h
index 7c24dea..069a6ee 100644
--- a/hwc2_device/HwcDisplay.h
+++ b/hwc2_device/HwcDisplay.h
@@ -247,6 +247,8 @@
 
   auto getDisplayPhysicalOrientation() -> std::optional<PanelOrientation>;
 
+  bool NeedsClientLayerUpdate() const;
+
  private:
   AtomicCommitArgs CreateModesetCommit(
       const HwcDisplayConfig *config,
diff --git a/hwc3/ComposerClient.cpp b/hwc3/ComposerClient.cpp
index 63a3d84..5da2c19 100644
--- a/hwc3/ComposerClient.cpp
+++ b/hwc3/ComposerClient.cpp
@@ -713,10 +713,12 @@
     display->SetColorTransformMatrix(ctm.value());
   }
 
+  bool shall_present_now = false;
+
+  DisplayChanges changes{};
   if (command.validateDisplay || command.presentOrValidateDisplay) {
     std::vector<HwcDisplay::ChangedLayer>
         changed_layers = display->ValidateStagedComposition();
-    DisplayChanges changes{};
     for (auto [layer_id, composition_type] : changed_layers) {
       changes.AddLayerCompositionChange(command.display, layer_id,
                                         static_cast<Composition>(
@@ -727,21 +729,23 @@
     hwc3_display->must_validate = false;
 
     // TODO: DisplayRequests are not implemented.
+  }
 
-    /* TODO: Add check if it's possible to skip display validation for
-     * presentOrValidateDisplay */
-    if (command.presentOrValidateDisplay) {
-      cmd_result_writer_
-          ->AddPresentOrValidateResult(display_id,
-                                       PresentOrValidate::Result::Validated);
+  if (command.presentOrValidateDisplay) {
+    auto result = PresentOrValidate::Result::Validated;
+    if (!display->NeedsClientLayerUpdate() && !changes.HasAnyChanges()) {
+      ALOGV("Skipping SF roundtrip for display %" PRId64, display_id);
+      result = PresentOrValidate::Result::Presented;
+      shall_present_now = true;
     }
+    cmd_result_writer_->AddPresentOrValidateResult(display_id, result);
   }
 
   if (command.acceptDisplayChanges) {
     display->AcceptValidatedComposition();
   }
 
-  if (command.presentDisplay) {
+  if (command.presentDisplay || shall_present_now) {
     auto hwc3_display = DrmHwcThree::GetHwc3Display(*display);
     if (hwc3_display->must_validate) {
       cmd_result_writer_->AddError(hwc3::Error::kNotValidated);