Identify a PIP window and implement hole punch
Bug: 163076219
Test: manual
Test: I53fc851eca876d44ba7cb9347f1c62d659c38932
In order to use a hole punch + device compositing to render a PIP, we
need to find a PIP that follows a CachedSet. A PIP (or, more generally,
a layer that we can render in this way in order to use device
composition more frequently) must have rounded corners and a buffer.
Add LayerFE::hasRoundedCorners (and implement in Layer) to allow
identifying whether a layer has rounded corners.
If a CachedSet has a hole punch layer, use a modified version of its
LayerSettings to draw a transparent round rect into its buffer.
Only active if caching and debug.sf.enable_hole_punch_pip are enabled
and PIPs use rounded corners (as in frameworks/base change
Ib0f61bbcbee6ead82beec2b149b0892836492b78).
Change-Id: I56e2a6debce6ede4bebfcbd32bffa01c20461542
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index 1fd07b0..791e7db 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -138,6 +138,9 @@
// Gets the sequence number: a serial number that uniquely identifies a Layer
virtual int32_t getSequence() const = 0;
+
+ // Whether the layer should be rendered with rounded corners.
+ virtual bool hasRoundedCorners() const = 0;
};
// TODO(b/121291683): Specialize std::hash<> for sp<T> so these and others can
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
index 53f4a30..7f068d4 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
@@ -105,12 +105,23 @@
void dump(std::string& result) const;
+ // Whether this represents a single layer with a buffer and rounded corners.
+ // If it is, we can draw it by placing it behind another CachedSet and
+ // punching a hole.
+ bool requiresHolePunch() const;
+
+ // Add a layer that will be drawn behind this one. ::render() will render a
+ // hole in this CachedSet's buffer, allowing the supplied layer to peek
+ // through. Must be called before ::render().
+ void addHolePunchLayer(const LayerState*);
+
private:
CachedSet() = default;
const NonBufferHash mFingerprint;
std::chrono::steady_clock::time_point mLastUpdate = std::chrono::steady_clock::now();
std::vector<Layer> mLayers;
+ const LayerState* mHolePunchLayer = nullptr;
Rect mBounds = Rect::EMPTY_RECT;
Region mVisibleRegion;
size_t mAge = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
index 2f2ad4c..942592a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
@@ -36,7 +36,8 @@
class Flattener {
public:
- Flattener(Predictor& predictor) : mPredictor(predictor) {}
+ Flattener(Predictor& predictor, bool enableHolePunch = false)
+ : mEnableHolePunch(enableHolePunch), mPredictor(predictor) {}
void setDisplaySize(ui::Size size) { mDisplaySize = size; }
@@ -61,6 +62,7 @@
void buildCachedSets(std::chrono::steady_clock::time_point now);
+ const bool mEnableHolePunch;
Predictor& mPredictor;
ui::Size mDisplaySize;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
index e6d2b63..c2037a8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
@@ -41,7 +41,7 @@
// as a more efficient representation of parts of the layer stack.
class Planner {
public:
- Planner() : mFlattener(mPredictor) {}
+ Planner();
void setDisplaySize(ui::Size);
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index dde8999..d215bda 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -43,6 +43,7 @@
MOCK_CONST_METHOD0(getDebugName, const char*());
MOCK_CONST_METHOD0(getSequence, int32_t());
+ MOCK_CONST_METHOD0(hasRoundedCorners, bool());
};
} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index 9955e29..238282e 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -193,6 +193,23 @@
std::transform(layerSettings.cbegin(), layerSettings.cend(),
std::back_inserter(layerSettingsPointers),
[](const renderengine::LayerSettings& settings) { return &settings; });
+ renderengine::LayerSettings holePunchSettings;
+ if (mHolePunchLayer) {
+ auto clientCompositionList =
+ mHolePunchLayer->getOutputLayer()->getLayerFE().prepareClientCompositionList(
+ targetSettings);
+ // Assume that the final layer contains the buffer that we want to
+ // replace with a hole punch.
+ holePunchSettings = clientCompositionList.back();
+ LOG_ALWAYS_FATAL_IF(!holePunchSettings.source.buffer.buffer, "Expected to have a buffer!");
+ // This mimics Layer::prepareClearClientComposition
+ holePunchSettings.source.buffer.buffer = nullptr;
+ holePunchSettings.source.solidColor = half3(0.0f, 0.0f, 0.0f);
+ holePunchSettings.disableBlending = true;
+ holePunchSettings.alpha = 0.0f;
+ holePunchSettings.name = std::string("hole punch layer");
+ layerSettingsPointers.push_back(&holePunchSettings);
+ }
if (sDebugHighlighLayers) {
highlight = {
@@ -241,6 +258,26 @@
}
}
+bool CachedSet::requiresHolePunch() const {
+ // In order for the hole punch to be beneficial, the layer must be updating
+ // regularly, meaning it should not have been merged with other layers.
+ if (getLayerCount() != 1) {
+ return false;
+ }
+
+ // There is no benefit to a hole punch unless the layer has a buffer.
+ if (!mLayers[0].getBuffer()) {
+ return false;
+ }
+
+ const auto& layerFE = mLayers[0].getState()->getOutputLayer()->getLayerFE();
+ return layerFE.hasRoundedCorners();
+}
+
+void CachedSet::addHolePunchLayer(const LayerState* layerState) {
+ mHolePunchLayer = layerState;
+}
+
void CachedSet::dump(std::string& result) const {
const auto now = std::chrono::steady_clock::now();
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index 9c9649c..960d8e1 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -298,6 +298,11 @@
std::vector<Run> runs;
bool isPartOfRun = false;
+
+ // Keep track of the layer that follows a run. It's possible that we will
+ // render it with a hole-punch.
+ const CachedSet* holePunchLayer = nullptr;
+
for (auto currentSet = mLayers.cbegin(); currentSet != mLayers.cend(); ++currentSet) {
if (now - currentSet->getLastUpdate() > kActiveLayerTimeout) {
// Layer is inactive
@@ -312,10 +317,20 @@
isPartOfRun = true;
}
}
- } else {
+ } else if (isPartOfRun) {
// Runs must be at least 2 sets long or there's nothing to combine
- if (isPartOfRun && runs.back().start->getLayerCount() == runs.back().length) {
+ if (runs.back().start->getLayerCount() == runs.back().length) {
runs.pop_back();
+ } else {
+ // The prior run contained at least two sets. Currently, we'll
+ // only possibly merge a single run, so only keep track of a
+ // holePunchLayer if this is the first run.
+ if (runs.size() == 1) {
+ holePunchLayer = &(*currentSet);
+ }
+
+ // TODO(b/185114532: Break out of the loop? We may find more runs, but we
+ // won't do anything with them.
}
isPartOfRun = false;
@@ -341,6 +356,12 @@
mNewCachedSet->append(*currentSet);
}
+ if (mEnableHolePunch && holePunchLayer && holePunchLayer->requiresHolePunch()) {
+ // Add the pip layer to mNewCachedSet, but in a special way - it should
+ // replace the buffer with a clear round rect.
+ mNewCachedSet->addHolePunchLayer(holePunchLayer->getFirstLayer().getState());
+ }
+
// TODO(b/181192467): Actually compute new LayerState vector and corresponding hash for each run
mPredictor.getPredictedPlan({}, 0);
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
index 3a2534b..7d2bf06 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
@@ -19,12 +19,17 @@
#undef LOG_TAG
#define LOG_TAG "Planner"
+#include <android-base/properties.h>
#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <compositionengine/impl/planner/Planner.h>
namespace android::compositionengine::impl::planner {
+Planner::Planner()
+ : mFlattener(mPredictor,
+ base::GetBoolProperty(std::string("debug.sf.enable_hole_punch_pip"), false)) {}
+
void Planner::setDisplaySize(ui::Size size) {
mFlattener.setDisplaySize(size);
}
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 9f3ea9a..688a2c3 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -600,6 +600,8 @@
// ignored.
virtual RoundedCornerState getRoundedCornerState() const;
+ bool hasRoundedCorners() const override { return getRoundedCornerState().radius > .0f; }
+
virtual PixelFormat getPixelFormat() const { return PIXEL_FORMAT_NONE; }
/**
* Return whether this layer needs an input info. For most layer types