SF: Add initial Planner infrastructure
Adds infrastructure for the SF Planner, which will support layer
caching/flattening and composition strategy prediction.
Bug: 158790260
Test: atest libcompositionengine_test libsurfaceflinger_unittest
Change-Id: I0d3027cea073fe25f269f3d5e83fe621dfbe7b2b
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 3907ac5..709d7c9 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -27,6 +27,9 @@
#include <compositionengine/impl/OutputCompositionState.h>
#include <compositionengine/impl/OutputLayer.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <compositionengine/impl/planner/Planner.h>
+
+#include <SurfaceFlingerProperties.sysprop.h>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
@@ -38,6 +41,7 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion"
+#include <android-base/properties.h>
#include <ui/DebugUtils.h>
#include <ui/HdrCapabilities.h>
#include <utils/Trace.h>
@@ -50,6 +54,18 @@
namespace impl {
+Output::Output() {
+ const bool enableLayerCaching = [] {
+ const bool enable =
+ android::sysprop::SurfaceFlingerProperties::enable_layer_caching().value_or(false);
+ return base::GetBoolProperty(std::string("debug.sf.enable_layer_caching"), enable);
+ }();
+
+ if (enableLayerCaching) {
+ mPlanner = std::make_unique<planner::Planner>();
+ }
+}
+
namespace {
template <typename T>
@@ -269,6 +285,15 @@
}
}
+void Output::dumpPlannerInfo(const Vector<String16>& args, std::string& out) const {
+ if (!mPlanner) {
+ base::StringAppendF(&out, "Planner is disabled\n");
+ return;
+ }
+ base::StringAppendF(&out, "Planner info for display [%s]\n", mName.c_str());
+ mPlanner->dump(args, out);
+}
+
compositionengine::DisplayColorProfile* Output::getDisplayColorProfile() const {
return mDisplayColorProfile.get();
}
@@ -368,7 +393,9 @@
ALOGV(__FUNCTION__);
updateColorProfile(refreshArgs);
- updateAndWriteCompositionState(refreshArgs);
+ updateCompositionState(refreshArgs);
+ planComposition();
+ writeCompositionState(refreshArgs);
setColorTransform(refreshArgs);
beginFrame();
prepareFrame();
@@ -632,8 +659,7 @@
}
}
-void Output::updateAndWriteCompositionState(
- const compositionengine::CompositionRefreshArgs& refreshArgs) {
+void Output::updateCompositionState(const compositionengine::CompositionRefreshArgs& refreshArgs) {
ATRACE_CALL();
ALOGV(__FUNCTION__);
@@ -653,8 +679,29 @@
if (mLayerRequestingBackgroundBlur == layer) {
forceClientComposition = false;
}
+ }
+}
- // Send the updated state to the HWC, if appropriate.
+void Output::planComposition() {
+ if (!mPlanner || !getState().isEnabled) {
+ return;
+ }
+
+ ATRACE_CALL();
+ ALOGV(__FUNCTION__);
+
+ mPlanner->plan(getOutputLayersOrderedByZ());
+}
+
+void Output::writeCompositionState(const compositionengine::CompositionRefreshArgs& refreshArgs) {
+ ATRACE_CALL();
+ ALOGV(__FUNCTION__);
+
+ if (!getState().isEnabled) {
+ return;
+ }
+
+ for (auto* layer : getOutputLayersOrderedByZ()) {
layer->writeStateToHWC(refreshArgs.updatingGeometryThisFrame);
}
}
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
new file mode 100644
index 0000000..7cf4819
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#include <compositionengine/impl/planner/LayerState.h>
+
+namespace android::compositionengine::impl::planner {
+
+LayerState::LayerState(compositionengine::OutputLayer* layer) : mOutputLayer(layer) {
+ update(layer);
+}
+
+Flags<LayerStateField> LayerState::update(compositionengine::OutputLayer* layer) {
+ ALOGE_IF(layer != mOutputLayer, "[%s] Expected mOutputLayer to never change", __func__);
+
+ Flags<LayerStateField> differences;
+
+ // Update the unique fields as well, since we have to set them at least
+ // once from the OutputLayer
+ differences |= mId.update(layer);
+ differences |= mName.update(layer);
+
+ for (StateInterface* field : getNonUniqueFields()) {
+ differences |= field->update(layer);
+ }
+
+ return differences;
+}
+
+size_t LayerState::getHash(
+ Flags<LayerStateField> skipFields = static_cast<LayerStateField>(0)) const {
+ size_t hash = 0;
+ for (const StateInterface* field : getNonUniqueFields()) {
+ android::hashCombineSingleHashed(hash, field->getHash(skipFields));
+ }
+
+ return hash;
+}
+
+Flags<LayerStateField> LayerState::getDifferingFields(
+ const LayerState& other,
+ Flags<LayerStateField> skipFields = static_cast<LayerStateField>(0)) const {
+ Flags<LayerStateField> differences;
+ auto myFields = getNonUniqueFields();
+ auto otherFields = other.getNonUniqueFields();
+ for (size_t i = 0; i < myFields.size(); ++i) {
+ if (skipFields.test(myFields[i]->getField())) {
+ continue;
+ }
+
+ differences |= myFields[i]->getFieldIfDifferent(otherFields[i]);
+ }
+
+ return differences;
+}
+
+void LayerState::dump(std::string& result) const {
+ for (const StateInterface* field : getNonUniqueFields()) {
+ if (auto viewOpt = flag_name(field->getField()); viewOpt) {
+ base::StringAppendF(&result, " %16s: ", std::string(*viewOpt).c_str());
+ } else {
+ result.append("<UNKNOWN FIELD>:\n");
+ }
+
+ bool first = true;
+ for (const std::string& line : field->toStrings()) {
+ base::StringAppendF(&result, "%s%s\n", first ? "" : " ",
+ line.c_str());
+ first = false;
+ }
+ }
+ result.append("\n");
+}
+
+std::optional<std::string> LayerState::compare(const LayerState& other) const {
+ std::string result;
+
+ const auto& thisFields = getNonUniqueFields();
+ const auto& otherFields = other.getNonUniqueFields();
+ for (size_t f = 0; f < thisFields.size(); ++f) {
+ const auto& thisField = thisFields[f];
+ const auto& otherField = otherFields[f];
+ // Skip comparing buffers
+ if (thisField->getField() == LayerStateField::Buffer) {
+ continue;
+ }
+
+ if (thisField->equals(otherField)) {
+ continue;
+ }
+
+ if (auto viewOpt = flag_name(thisField->getField()); viewOpt) {
+ base::StringAppendF(&result, " %16s: ", std::string(*viewOpt).c_str());
+ } else {
+ result.append("<UNKNOWN FIELD>:\n");
+ }
+
+ const auto& thisStrings = thisField->toStrings();
+ const auto& otherStrings = otherField->toStrings();
+ bool first = true;
+ for (size_t line = 0; line < std::max(thisStrings.size(), otherStrings.size()); ++line) {
+ if (!first) {
+ result.append(" ");
+ }
+ first = false;
+
+ if (line < thisStrings.size()) {
+ base::StringAppendF(&result, "%-48.48s", thisStrings[line].c_str());
+ } else {
+ result.append(" ");
+ }
+
+ if (line < otherStrings.size()) {
+ base::StringAppendF(&result, "%-48.48s", otherStrings[line].c_str());
+ } else {
+ result.append(" ");
+ }
+ result.append("\n");
+ }
+ }
+
+ return result.empty() ? std::nullopt : std::make_optional(result);
+}
+
+bool operator==(const LayerState& lhs, const LayerState& rhs) {
+ return lhs.mId == rhs.mId && lhs.mName == rhs.mName && lhs.mDisplayFrame == rhs.mDisplayFrame &&
+ lhs.mSourceCrop == rhs.mSourceCrop && lhs.mZOrder == rhs.mZOrder &&
+ lhs.mBufferTransform == rhs.mBufferTransform && lhs.mBlendMode == rhs.mBlendMode &&
+ lhs.mAlpha == rhs.mAlpha && lhs.mVisibleRegion == rhs.mVisibleRegion &&
+ lhs.mDataspace == rhs.mDataspace && lhs.mColorTransform == rhs.mColorTransform &&
+ lhs.mCompositionType == rhs.mCompositionType &&
+ lhs.mSidebandStream == rhs.mSidebandStream && lhs.mBuffer == rhs.mBuffer &&
+ (lhs.mCompositionType.get() != hal::Composition::SOLID_COLOR ||
+ lhs.mSolidColor == rhs.mSolidColor);
+}
+
+NonBufferHash getNonBufferHash(const std::vector<const LayerState*>& layers) {
+ size_t hash = 0;
+ for (const auto layer : layers) {
+ android::hashCombineSingleHashed(hash, layer->getHash(LayerStateField::Buffer));
+ }
+
+ return hash;
+}
+
+} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
new file mode 100644
index 0000000..30f4892
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2021 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.
+ */
+
+// #define LOG_NDEBUG 0
+
+#undef LOG_TAG
+#define LOG_TAG "Planner"
+
+#include <compositionengine/impl/planner/Planner.h>
+
+namespace android::compositionengine::impl::planner {
+
+void Planner::plan(
+ compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers) {
+ std::unordered_set<LayerId> removedLayers;
+ removedLayers.reserve(mPreviousLayers.size());
+
+ std::transform(mPreviousLayers.begin(), mPreviousLayers.end(),
+ std::inserter(removedLayers, removedLayers.begin()),
+ [](const auto& layer) { return layer.first; });
+
+ for (auto layer : layers) {
+ LayerId id = layer->getLayerFE().getSequence();
+ if (const auto layerEntry = mPreviousLayers.find(id); layerEntry != mPreviousLayers.end()) {
+ // Track changes from previous info
+ LayerState& state = layerEntry->second;
+ Flags<LayerStateField> differences = state.update(layer);
+ if (differences.get() != 0) {
+ ALOGV("Layer %s changed: %s", state.getName().c_str(),
+ differences.string().c_str());
+
+ if (differences.test(LayerStateField::Buffer)) {
+ state.resetFramesSinceBufferUpdate();
+ } else {
+ state.incrementFramesSinceBufferUpdate();
+ }
+ }
+ } else {
+ LayerState state(layer);
+ ALOGV("Added layer %s", state.getName().c_str());
+ mPreviousLayers.emplace(std::make_pair(id, std::move(state)));
+ }
+
+ if (const auto found = removedLayers.find(id); found != removedLayers.end()) {
+ removedLayers.erase(found);
+ }
+ }
+
+ for (LayerId removedLayer : removedLayers) {
+ if (const auto layerEntry = mPreviousLayers.find(removedLayer);
+ layerEntry != mPreviousLayers.end()) {
+ const auto& [id, state] = *layerEntry;
+ ALOGV("Removed layer %s", state.getName().c_str());
+ mPreviousLayers.erase(removedLayer);
+ }
+ }
+}
+
+void Planner::dump(const Vector<String16>& /* args */, std::string& result) {
+ result.append("Dumping Planner state");
+}
+
+} // namespace android::compositionengine::impl::planner