Add CachingHint into SurfaceFlinger
Some system layers may need to be excluded from surfaceflinger caching
for a variety of reasons, including power or image quality
considerations. This provides a mechanism for those system layers to
never be cached on the GPU.
Bug: 259311918
Test: libcompositionengine_test
Test: run test app and inspect planner state
Change-Id: I35418ad5a41cb2546e3b27b0af290bf3c31a0180
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 32684fc..0b4d20c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -18,6 +18,7 @@
#include <cstdint>
+#include <android/gui/CachingHint.h>
#include <gui/HdrMetadata.h>
#include <math/mat4.h>
#include <ui/BlurRegion.h>
@@ -213,6 +214,7 @@
float desiredSdrHdrRatio = 1.f;
bool isInternalDisplayOverlay = false;
+ gui::CachingHint cachingHint = gui::CachingHint::Enabled;
virtual ~LayerFECompositionState();
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
index 24a7744..d26ca9d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
@@ -149,6 +149,9 @@
bool hasSolidColorLayers() const;
+ // True if any layer in this cached set has CachingHint::Disabled
+ bool cachingHintExcludesLayers() const;
+
private:
const NonBufferHash mFingerprint;
std::chrono::steady_clock::time_point mLastUpdate = std::chrono::steady_clock::now();
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
index e309442..d5c488e 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
@@ -73,6 +73,7 @@
BackgroundBlurRadius = 1u << 17,
BlurRegions = 1u << 18,
HasProtectedContent = 1u << 19,
+ CachingHint = 1u << 20,
};
// clang-format on
@@ -250,6 +251,8 @@
bool isProtected() const { return mIsProtected.get(); }
+ gui::CachingHint getCachingHint() const { return mCachingHint.get(); }
+
bool hasSolidColorCompositionType() const {
return getOutputLayer()->getLayerFE().getCompositionState()->compositionType ==
aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR;
@@ -487,7 +490,15 @@
return layer->getLayerFE().getCompositionState()->hasProtectedContent;
}};
- static const constexpr size_t kNumNonUniqueFields = 18;
+ OutputLayerState<gui::CachingHint, LayerStateField::CachingHint>
+ mCachingHint{[](auto layer) {
+ return layer->getLayerFE().getCompositionState()->cachingHint;
+ },
+ [](const gui::CachingHint& cachingHint) {
+ return std::vector<std::string>{toString(cachingHint)};
+ }};
+
+ static const constexpr size_t kNumNonUniqueFields = 19;
std::array<StateInterface*, kNumNonUniqueFields> getNonUniqueFields() {
std::array<const StateInterface*, kNumNonUniqueFields> constFields =
@@ -501,13 +512,11 @@
}
std::array<const StateInterface*, kNumNonUniqueFields> getNonUniqueFields() const {
- return {
- &mDisplayFrame, &mSourceCrop, &mBufferTransform, &mBlendMode,
+ return {&mDisplayFrame, &mSourceCrop, &mBufferTransform, &mBlendMode,
&mAlpha, &mLayerMetadata, &mVisibleRegion, &mOutputDataspace,
&mPixelFormat, &mColorTransform, &mCompositionType, &mSidebandStream,
&mBuffer, &mSolidColor, &mBackgroundBlurRadius, &mBlurRegions,
- &mFrameNumber, &mIsProtected,
- };
+ &mFrameNumber, &mIsProtected, &mCachingHint};
}
};
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
index 531b659..615d04b 100644
--- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
@@ -126,6 +126,7 @@
dumpVal(out, "desired sdr/hdr ratio", desiredSdrHdrRatio);
}
dumpVal(out, "colorTransform", colorTransform);
+ dumpVal(out, "caching hint", toString(cachingHint));
out.append("\n");
}
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index ed9a88d..a00ce57 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -393,6 +393,18 @@
});
}
+bool CachedSet::cachingHintExcludesLayers() const {
+ const bool shouldExcludeLayers =
+ std::any_of(mLayers.cbegin(), mLayers.cend(), [](const Layer& layer) {
+ return layer.getState()->getCachingHint() == gui::CachingHint::Disabled;
+ });
+
+ LOG_ALWAYS_FATAL_IF(shouldExcludeLayers && getLayerCount() > 1,
+ "CachedSet is invalid: should be excluded but contains %zu layers",
+ getLayerCount());
+ return shouldExcludeLayers;
+}
+
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 9175dd0..13b6307 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -413,6 +413,7 @@
for (auto currentSet = mLayers.cbegin(); currentSet != mLayers.cend(); ++currentSet) {
bool layerIsInactive = now - currentSet->getLastUpdate() > mTunables.mActiveLayerTimeout;
const bool layerHasBlur = currentSet->hasBlurBehind();
+ const bool layerDeniedFromCaching = currentSet->cachingHintExcludesLayers();
// Layers should also be considered inactive whenever their framerate is lower than 1fps.
if (!layerIsInactive && currentSet->getLayerCount() == kNumLayersFpsConsideration) {
@@ -424,7 +425,8 @@
}
}
- if (layerIsInactive && (firstLayer || runHasFirstLayer || !layerHasBlur) &&
+ if (!layerDeniedFromCaching && layerIsInactive &&
+ (firstLayer || runHasFirstLayer || !layerHasBlur) &&
!currentSet->hasUnsupportedDataspace()) {
if (isPartOfRun) {
builder.increment();
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
index d5d688e..ca5ba69 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -577,6 +577,20 @@
cachedSet.append(CachedSet(layer3));
}
+TEST_F(CachedSetTest, cachingHintIncludesLayersByDefault) {
+ CachedSet cachedSet(*mTestLayers[0]->cachedSetLayer.get());
+ EXPECT_FALSE(cachedSet.cachingHintExcludesLayers());
+}
+
+TEST_F(CachedSetTest, cachingHintExcludesLayersWhenDisabled) {
+ CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+ mTestLayers[0]->layerFECompositionState.cachingHint = gui::CachingHint::Disabled;
+ mTestLayers[0]->layerState->update(&mTestLayers[0]->outputLayer);
+
+ CachedSet cachedSet(layer1);
+ EXPECT_TRUE(cachedSet.cachingHintExcludesLayers());
+}
+
TEST_F(CachedSetTest, holePunch_requiresBuffer) {
CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
auto& layerFECompositionState = mTestLayers[0]->layerFECompositionState;
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index 86cfee6..778a0a8 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -1112,6 +1112,55 @@
true);
}
+TEST_F(FlattenerTest, flattenLayers_skipsLayersDisabledFromCaching) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+ const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+
+ auto& layerState2 = mTestLayers[1]->layerState;
+ const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;
+
+ // The third layer has a CachingHint that prevents caching from running
+ auto& layerState3 = mTestLayers[2]->layerState;
+ const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;
+ mTestLayers[2]->layerFECompositionState.cachingHint = gui::CachingHint::Disabled;
+ mTestLayers[2]->layerState->update(&mTestLayers[2]->outputLayer);
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ layerState3.get(),
+ };
+
+ initializeFlattener(layers);
+
+ mTime += 200ms;
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+
+ // This will render a CachedSet.
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
+
+ // We've rendered a CachedSet, but we haven't merged it in.
+ EXPECT_EQ(nullptr, overrideBuffer1);
+ EXPECT_EQ(nullptr, overrideBuffer2);
+ EXPECT_EQ(nullptr, overrideBuffer3);
+
+ // This time we merge the CachedSet in, so we have a new hash, and we should
+ // only have two sets.
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
+ initializeOverrideBuffer(layers);
+ EXPECT_NE(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
+
+ EXPECT_NE(nullptr, overrideBuffer1);
+ EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+ EXPECT_EQ(nullptr, overrideBuffer3);
+}
+
TEST_F(FlattenerTest, flattenLayers_skipsBT601_625) {
auto& layerState1 = mTestLayers[0]->layerState;
const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
index 47b6820..044917e 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
@@ -994,6 +994,45 @@
EXPECT_TRUE(mLayerState->hasBlurBehind());
}
+TEST_F(LayerStateTest, updateCachingHint) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.cachingHint = gui::CachingHint::Enabled;
+ setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make();
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.cachingHint = gui::CachingHint::Disabled;
+ setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ ftl::Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(ftl::Flags<LayerStateField>(LayerStateField::CachingHint), updates);
+}
+
+TEST_F(LayerStateTest, compareCachingHint) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.cachingHint = gui::CachingHint::Enabled;
+ setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make();
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.cachingHint = gui::CachingHint::Disabled;
+ setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::CachingHint);
+
+ EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+ EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
TEST_F(LayerStateTest, dumpDoesNotCrash) {
OutputLayerCompositionState outputLayerCompositionState;
LayerFECompositionState layerFECompositionState;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index c9aeb24..d740350 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -727,6 +727,7 @@
snapshot.dimmingEnabled = requested.dimmingEnabled;
snapshot.layerOpaqueFlagSet =
(requested.flags & layer_state_t::eLayerOpaque) == layer_state_t::eLayerOpaque;
+ snapshot.cachingHint = requested.cachingHint;
}
if (forceUpdate || snapshot.changes.any(RequestedLayerState::Changes::Content)) {
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index 2834084..09523d3 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -137,6 +137,7 @@
dataspace = ui::Dataspace::V0_SRGB;
gameMode = gui::GameMode::Unsupported;
requestedFrameRate = {};
+ cachingHint = gui::CachingHint::Enabled;
}
void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerState) {
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 084d9b9..dac0916 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -566,6 +566,7 @@
: Hwc2::IComposerClient::BlendMode::COVERAGE;
}
+ // Please keep in sync with LayerSnapshotBuilder
auto* snapshot = editLayerSnapshot();
snapshot->outputFilter = getOutputFilter();
snapshot->isVisible = isVisible();
@@ -592,6 +593,7 @@
const auto& drawingState{getDrawingState()};
auto* snapshot = editLayerSnapshot();
+ // Please keep in sync with LayerSnapshotBuilder
snapshot->geomBufferSize = getBufferSize(drawingState);
snapshot->geomContentCrop = getBufferCrop();
snapshot->geomCrop = getCrop(drawingState);
@@ -624,6 +626,7 @@
void Layer::preparePerFrameCompositionState() {
const auto& drawingState{getDrawingState()};
+ // Please keep in sync with LayerSnapshotBuilder
auto* snapshot = editLayerSnapshot();
snapshot->forceClientComposition = false;
@@ -637,6 +640,7 @@
snapshot->dimmingEnabled = isDimmingEnabled();
snapshot->currentSdrHdrRatio = getCurrentSdrHdrRatio();
snapshot->desiredSdrHdrRatio = getDesiredSdrHdrRatio();
+ snapshot->cachingHint = getCachingHint();
const bool usesRoundedCorners = hasRoundedCorners();
@@ -666,8 +670,9 @@
}
void Layer::preparePerFrameBufferCompositionState() {
- // Sideband layers
+ // Please keep in sync with LayerSnapshotBuilder
auto* snapshot = editLayerSnapshot();
+ // Sideband layers
if (snapshot->sidebandStream.get() && !snapshot->sidebandStreamHasFrame) {
snapshot->compositionType =
aidl::android::hardware::graphics::composer3::Composition::SIDEBAND;
@@ -690,6 +695,7 @@
}
void Layer::preparePerFrameEffectsCompositionState() {
+ // Please keep in sync with LayerSnapshotBuilder
auto* snapshot = editLayerSnapshot();
snapshot->color = getColor();
snapshot->compositionType =
@@ -698,6 +704,7 @@
void Layer::prepareCursorCompositionState() {
const State& drawingState{getDrawingState()};
+ // Please keep in sync with LayerSnapshotBuilder
auto* snapshot = editLayerSnapshot();
// Apply the layer's transform, followed by the display's global transform
@@ -3091,6 +3098,14 @@
return true;
}
+bool Layer::setCachingHint(gui::CachingHint cachingHint) {
+ if (mDrawingState.cachingHint == cachingHint) return false;
+ mDrawingState.cachingHint = cachingHint;
+ mDrawingState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
bool Layer::setHdrMetadata(const HdrMetadata& hdrMetadata) {
if (mDrawingState.hdrMetadata == hdrMetadata) return false;
mDrawingState.hdrMetadata = hdrMetadata;
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 2955daf..7d40774 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -227,6 +227,7 @@
bool dimmingEnabled = true;
float currentSdrHdrRatio = 1.f;
float desiredSdrHdrRatio = 1.f;
+ gui::CachingHint cachingHint = gui::CachingHint::Enabled;
};
explicit Layer(const LayerCreationArgs& args);
@@ -296,6 +297,7 @@
virtual bool isDimmingEnabled() const { return getDrawingState().dimmingEnabled; }
float getDesiredSdrHdrRatio() const { return getDrawingState().desiredSdrHdrRatio; }
float getCurrentSdrHdrRatio() const { return getDrawingState().currentSdrHdrRatio; }
+ gui::CachingHint getCachingHint() const { return getDrawingState().cachingHint; }
bool setTransform(uint32_t /*transform*/);
bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/);
@@ -305,6 +307,7 @@
std::optional<nsecs_t> /* dequeueTime */, const FrameTimelineInfo& /*info*/);
bool setDataspace(ui::Dataspace /*dataspace*/);
bool setExtendedRangeBrightness(float currentBufferRatio, float desiredRatio);
+ bool setCachingHint(gui::CachingHint cachingHint);
bool setHdrMetadata(const HdrMetadata& /*hdrMetadata*/);
bool setSurfaceDamageRegion(const Region& /*surfaceDamage*/);
bool setApi(int32_t /*api*/);
diff --git a/services/surfaceflinger/LayerFE.h b/services/surfaceflinger/LayerFE.h
index 01da019..c23bd31 100644
--- a/services/surfaceflinger/LayerFE.h
+++ b/services/surfaceflinger/LayerFE.h
@@ -16,6 +16,7 @@
#pragma once
+#include <android/gui/CachingHint.h>
#include <gui/LayerMetadata.h>
#include "FrontEnd/LayerSnapshot.h"
#include "compositionengine/LayerFE.h"
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 5f95859..5c8579c 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -4783,6 +4783,11 @@
flags |= eTraversalNeeded;
}
}
+ if (what & layer_state_t::eCachingHintChanged) {
+ if (layer->setCachingHint(s.cachingHint)) {
+ flags |= eTraversalNeeded;
+ }
+ }
if (what & layer_state_t::eHdrMetadataChanged) {
if (layer->setHdrMetadata(s.hdrMetadata)) flags |= eTraversalNeeded;
}