Add promise for buffer releaseFence to LayerFE.
Buffers can only be released when a release fence fires.
Instead of tracking and waiting for each layer's Futures to
complete, LayerFE can keep track of its own Future via a
promise of a fence. This cleans up the shared futures that
are currently being tracked and allow us to setup for
eventual goals to reduce the number of screenshot calls to
the main thread.
Tests using a virtual display are modified to use the
mirrorDisplay API with a unique layerstack.
Bug: b/294936197, b/285553970
Test: manual (screenshot, camera, rotation, picture-in-picture)
Test: presubmit
Test: atest CompositionEnginePostCompositionTest
Test: atest SurfaceFlinger_test
Change-Id: I3a0af2cd02d3d010a1ea7c860c5cb0bc20837ec2
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
index 7c10fa5..e32cc02 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
@@ -73,6 +73,9 @@
// TODO(b/121291683): These will become private/internal
virtual void preComposition(CompositionRefreshArgs&) = 0;
+ // Resolves any unfulfilled promises for release fences
+ virtual void postComposition(CompositionRefreshArgs&) = 0;
+
virtual FeatureFlags getFeatureFlags() const = 0;
// Debugging
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index a499928..4e080b3 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -133,6 +133,15 @@
uint64_t frameNumber = 0;
};
+ // Describes the states of the release fence. Checking the states allows checks
+ // to ensure that set_value() is not called on the same promise multiple times,
+ // and can indicate if the promise has been fulfilled.
+ enum class ReleaseFencePromiseStatus {
+ UNINITIALIZED, // Promise not created
+ INITIALIZED, // Promise created, fence has not been set
+ FULFILLED // Promise fulfilled, fence is set
+ };
+
// Returns the LayerSettings to pass to RenderEngine::drawLayers. The state may contain shadows
// casted by the layer or the content of the layer itself. If the layer does not render then an
// empty optional will be returned.
@@ -142,6 +151,19 @@
// Called after the layer is displayed to update the presentation fence
virtual void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack layerStack) = 0;
+ // Initializes a promise for a buffer release fence and provides the future for that
+ // fence. This should only be called when a promise has not yet been created, or
+ // after the previous promise has already been fulfilled. Attempting to call this
+ // when an existing promise is INITIALIZED will fail because the promise has not
+ // yet been fulfilled.
+ virtual ftl::Future<FenceResult> createReleaseFenceFuture() = 0;
+
+ // Sets promise with its buffer's release fence
+ virtual void setReleaseFence(const FenceResult& releaseFence) = 0;
+
+ // Checks if the buffer's release fence has been set
+ virtual LayerFE::ReleaseFencePromiseStatus getReleaseFencePromiseStatus() = 0;
+
// Gets some kind of identifier for the layer for debug purposes.
virtual const char* getDebugName() const = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
index c699557..45208dd 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
@@ -48,6 +48,8 @@
void preComposition(CompositionRefreshArgs&) override;
+ void postComposition(CompositionRefreshArgs&) override;
+
FeatureFlags getFeatureFlags() const override;
// Debugging
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
index 9b2387b..a1b7282 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
@@ -52,6 +52,7 @@
MOCK_METHOD1(updateCursorAsync, void(CompositionRefreshArgs&));
MOCK_METHOD1(preComposition, void(CompositionRefreshArgs&));
+ MOCK_METHOD1(postComposition, void(CompositionRefreshArgs&));
MOCK_CONST_METHOD0(getFeatureFlags, FeatureFlags());
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index 1b8cc27..05a5d38 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -20,6 +20,7 @@
#include <compositionengine/LayerFECompositionState.h>
#include <gmock/gmock.h>
#include <ui/Fence.h>
+#include "ui/FenceResult.h"
namespace android::compositionengine::mock {
@@ -52,6 +53,9 @@
MOCK_METHOD(void, onLayerDisplayed, (ftl::SharedFuture<FenceResult>, ui::LayerStack),
(override));
+ MOCK_METHOD0(createReleaseFenceFuture, ftl::Future<FenceResult>());
+ MOCK_METHOD1(setReleaseFence, void(const FenceResult&));
+ MOCK_METHOD0(getReleaseFencePromiseStatus, LayerFE::ReleaseFencePromiseStatus());
MOCK_CONST_METHOD0(getDebugName, const char*());
MOCK_CONST_METHOD0(getSequence, int32_t());
MOCK_CONST_METHOD0(hasRoundedCorners, bool());
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
index b470208..4c77687 100644
--- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -162,6 +162,7 @@
future.get();
}
}
+ postComposition(args);
}
void CompositionEngine::updateCursorAsync(CompositionRefreshArgs& args) {
@@ -192,6 +193,34 @@
mNeedsAnotherUpdate = needsAnotherUpdate;
}
+// If a buffer is latched but the layer is not presented, such as when
+// obscured by another layer, the previous buffer needs to be released. We find
+// these buffers and fire a NO_FENCE to release it. This ensures that all
+// promises for buffer releases are fulfilled at the end of composition.
+void CompositionEngine::postComposition(CompositionRefreshArgs& args) {
+ if (FlagManager::getInstance().ce_fence_promise()) {
+ ATRACE_CALL();
+ ALOGV(__FUNCTION__);
+
+ for (auto& layerFE : args.layers) {
+ if (layerFE->getReleaseFencePromiseStatus() ==
+ LayerFE::ReleaseFencePromiseStatus::INITIALIZED) {
+ layerFE->setReleaseFence(Fence::NO_FENCE);
+ }
+ }
+
+ // List of layersWithQueuedFrames does not necessarily overlap with
+ // list of layers, so those layersWithQueuedFrames also need any
+ // unfulfilled promises to be resolved for completeness.
+ for (auto& layerFE : args.layersWithQueuedFrames) {
+ if (layerFE->getReleaseFencePromiseStatus() ==
+ LayerFE::ReleaseFencePromiseStatus::INITIALIZED) {
+ layerFE->setReleaseFence(Fence::NO_FENCE);
+ }
+ }
+ }
+}
+
FeatureFlags CompositionEngine::getFeatureFlags() const {
return {};
}
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 921e05d..7b5b49d 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -16,6 +16,7 @@
#include <SurfaceFlingerProperties.sysprop.h>
#include <android-base/stringprintf.h>
+#include <common/FlagManager.h>
#include <compositionengine/CompositionEngine.h>
#include <compositionengine/CompositionRefreshArgs.h>
#include <compositionengine/DisplayColorProfile.h>
@@ -1621,9 +1622,13 @@
releaseFence =
Fence::merge("LayerRelease", releaseFence, frame.clientTargetAcquireFence);
}
- layer->getLayerFE()
- .onLayerDisplayed(ftl::yield<FenceResult>(std::move(releaseFence)).share(),
- outputState.layerFilter.layerStack);
+ if (FlagManager::getInstance().ce_fence_promise()) {
+ layer->getLayerFE().setReleaseFence(releaseFence);
+ } else {
+ layer->getLayerFE()
+ .onLayerDisplayed(ftl::yield<FenceResult>(std::move(releaseFence)).share(),
+ outputState.layerFilter.layerStack);
+ }
}
// We've got a list of layers needing fences, that are disjoint with
@@ -1631,8 +1636,12 @@
// supply them with the present fence.
for (auto& weakLayer : mReleasedLayers) {
if (const auto layer = weakLayer.promote()) {
- layer->onLayerDisplayed(ftl::yield<FenceResult>(frame.presentFence).share(),
- outputState.layerFilter.layerStack);
+ if (FlagManager::getInstance().ce_fence_promise()) {
+ layer->setReleaseFence(frame.presentFence);
+ } else {
+ layer->onLayerDisplayed(ftl::yield<FenceResult>(frame.presentFence).share(),
+ outputState.layerFilter.layerStack);
+ }
}
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
index 042010e..639164d 100644
--- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
@@ -28,6 +28,7 @@
#include "MockHWComposer.h"
#include "TimeStats/TimeStats.h"
+#include "gmock/gmock.h"
#include <variant>
@@ -90,14 +91,16 @@
// These are the overridable functions CompositionEngine::present() may
// call, and have separate test coverage.
MOCK_METHOD1(preComposition, void(CompositionRefreshArgs&));
+ MOCK_METHOD1(postComposition, void(CompositionRefreshArgs&));
};
StrictMock<CompositionEnginePartialMock> mEngine;
};
TEST_F(CompositionEnginePresentTest, worksWithEmptyRequest) {
- // present() always calls preComposition()
+ // present() always calls preComposition() and postComposition()
EXPECT_CALL(mEngine, preComposition(Ref(mRefreshArgs)));
+ EXPECT_CALL(mEngine, postComposition(Ref(mRefreshArgs)));
mEngine.present(mRefreshArgs);
}
@@ -126,6 +129,9 @@
EXPECT_CALL(*mOutput3, present(Ref(mRefreshArgs)))
.WillOnce(Return(ftl::yield<std::monostate>({})));
+ // present() always calls postComposition()
+ EXPECT_CALL(mEngine, postComposition(Ref(mRefreshArgs)));
+
mRefreshArgs.outputs = {mOutput1, mOutput2, mOutput3};
mEngine.present(mRefreshArgs);
}
@@ -481,5 +487,29 @@
mEngine.present(mRefreshArgs);
}
+struct CompositionEnginePostCompositionTest : public CompositionEngineTest {
+ sp<StrictMock<mock::LayerFE>> mLayer1FE = sp<StrictMock<mock::LayerFE>>::make();
+ sp<StrictMock<mock::LayerFE>> mLayer2FE = sp<StrictMock<mock::LayerFE>>::make();
+ sp<StrictMock<mock::LayerFE>> mLayer3FE = sp<StrictMock<mock::LayerFE>>::make();
+};
+
+TEST_F(CompositionEnginePostCompositionTest, postCompositionReleasesAllFences) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, true);
+ ASSERT_TRUE(FlagManager::getInstance().ce_fence_promise());
+
+ EXPECT_CALL(*mLayer1FE, getReleaseFencePromiseStatus)
+ .WillOnce(Return(LayerFE::ReleaseFencePromiseStatus::FULFILLED));
+ EXPECT_CALL(*mLayer2FE, getReleaseFencePromiseStatus)
+ .WillOnce(Return(LayerFE::ReleaseFencePromiseStatus::FULFILLED));
+ EXPECT_CALL(*mLayer3FE, getReleaseFencePromiseStatus)
+ .WillOnce(Return(LayerFE::ReleaseFencePromiseStatus::INITIALIZED));
+ mRefreshArgs.layers = {mLayer1FE, mLayer2FE, mLayer3FE};
+
+ EXPECT_CALL(*mLayer1FE, setReleaseFence(_)).Times(0);
+ EXPECT_CALL(*mLayer2FE, setReleaseFence(_)).Times(0);
+ EXPECT_CALL(*mLayer3FE, setReleaseFence(_)).Times(1);
+
+ mEngine.postComposition(mRefreshArgs);
+}
} // namespace
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 799c7ed..ea1d8e8 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -3164,6 +3164,8 @@
}
TEST_F(OutputPostFramebufferTest, releaseFencesAreSentToLayerFE) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, false);
+ ASSERT_FALSE(FlagManager::getInstance().ce_fence_promise());
// Simulate getting release fences from each layer, and ensure they are passed to the
// front-end layer interface for each layer correctly.
@@ -3205,7 +3207,51 @@
mOutput.presentFrameAndReleaseLayers();
}
+TEST_F(OutputPostFramebufferTest, releaseFencesAreSetInLayerFE) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, true);
+ ASSERT_TRUE(FlagManager::getInstance().ce_fence_promise());
+ // Simulate getting release fences from each layer, and ensure they are passed to the
+ // front-end layer interface for each layer correctly.
+
+ mOutput.mState.isEnabled = true;
+
+ // Create three unique fence instances
+ sp<Fence> layer1Fence = sp<Fence>::make();
+ sp<Fence> layer2Fence = sp<Fence>::make();
+ sp<Fence> layer3Fence = sp<Fence>::make();
+
+ Output::FrameFences frameFences;
+ frameFences.layerFences.emplace(&mLayer1.hwc2Layer, layer1Fence);
+ frameFences.layerFences.emplace(&mLayer2.hwc2Layer, layer2Fence);
+ frameFences.layerFences.emplace(&mLayer3.hwc2Layer, layer3Fence);
+
+ EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences));
+ EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
+
+ // Compare the pointers values of each fence to make sure the correct ones
+ // are passed. This happens to work with the current implementation, but
+ // would not survive certain calls like Fence::merge() which would return a
+ // new instance.
+ EXPECT_CALL(*mLayer1.layerFE, setReleaseFence(_))
+ .WillOnce([&layer1Fence](FenceResult releaseFence) {
+ EXPECT_EQ(FenceResult(layer1Fence), releaseFence);
+ });
+ EXPECT_CALL(*mLayer2.layerFE, setReleaseFence(_))
+ .WillOnce([&layer2Fence](FenceResult releaseFence) {
+ EXPECT_EQ(FenceResult(layer2Fence), releaseFence);
+ });
+ EXPECT_CALL(*mLayer3.layerFE, setReleaseFence(_))
+ .WillOnce([&layer3Fence](FenceResult releaseFence) {
+ EXPECT_EQ(FenceResult(layer3Fence), releaseFence);
+ });
+
+ mOutput.presentFrameAndReleaseLayers();
+}
+
TEST_F(OutputPostFramebufferTest, releaseFencesIncludeClientTargetAcquireFence) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, false);
+ ASSERT_FALSE(FlagManager::getInstance().ce_fence_promise());
+
mOutput.mState.isEnabled = true;
mOutput.mState.usesClientComposition = true;
@@ -3228,7 +3274,35 @@
mOutput.presentFrameAndReleaseLayers();
}
+TEST_F(OutputPostFramebufferTest, setReleaseFencesIncludeClientTargetAcquireFence) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, true);
+ ASSERT_TRUE(FlagManager::getInstance().ce_fence_promise());
+
+ mOutput.mState.isEnabled = true;
+ mOutput.mState.usesClientComposition = true;
+
+ Output::FrameFences frameFences;
+ frameFences.clientTargetAcquireFence = sp<Fence>::make();
+ frameFences.layerFences.emplace(&mLayer1.hwc2Layer, sp<Fence>::make());
+ frameFences.layerFences.emplace(&mLayer2.hwc2Layer, sp<Fence>::make());
+ frameFences.layerFences.emplace(&mLayer3.hwc2Layer, sp<Fence>::make());
+
+ EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences));
+ EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
+
+ // Fence::merge is called, and since none of the fences are actually valid,
+ // Fence::NO_FENCE is returned and passed to each setReleaseFence() call.
+ // This is the best we can do without creating a real kernel fence object.
+ EXPECT_CALL(*mLayer1.layerFE, setReleaseFence).WillOnce(Return());
+ EXPECT_CALL(*mLayer2.layerFE, setReleaseFence).WillOnce(Return());
+ EXPECT_CALL(*mLayer3.layerFE, setReleaseFence).WillOnce(Return());
+ mOutput.presentFrameAndReleaseLayers();
+}
+
TEST_F(OutputPostFramebufferTest, releasedLayersSentPresentFence) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, false);
+ ASSERT_FALSE(FlagManager::getInstance().ce_fence_promise());
+
mOutput.mState.isEnabled = true;
mOutput.mState.usesClientComposition = true;
@@ -3276,6 +3350,54 @@
EXPECT_TRUE(mOutput.getReleasedLayersForTest().empty());
}
+TEST_F(OutputPostFramebufferTest, setReleasedLayersSentPresentFence) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, true);
+ ASSERT_TRUE(FlagManager::getInstance().ce_fence_promise());
+
+ mOutput.mState.isEnabled = true;
+ mOutput.mState.usesClientComposition = true;
+
+ // This should happen even if there are no (current) output layers.
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+
+ // Load up the released layers with some mock instances
+ sp<StrictMock<mock::LayerFE>> releasedLayer1 = sp<StrictMock<mock::LayerFE>>::make();
+ sp<StrictMock<mock::LayerFE>> releasedLayer2 = sp<StrictMock<mock::LayerFE>>::make();
+ sp<StrictMock<mock::LayerFE>> releasedLayer3 = sp<StrictMock<mock::LayerFE>>::make();
+ Output::ReleasedLayers layers;
+ layers.push_back(releasedLayer1);
+ layers.push_back(releasedLayer2);
+ layers.push_back(releasedLayer3);
+ mOutput.setReleasedLayers(std::move(layers));
+
+ // Set up a fake present fence
+ sp<Fence> presentFence = sp<Fence>::make();
+ Output::FrameFences frameFences;
+ frameFences.presentFence = presentFence;
+
+ EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences));
+ EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
+
+ // Each released layer should be given the presentFence.
+ EXPECT_CALL(*releasedLayer1, setReleaseFence(_))
+ .WillOnce([&presentFence](FenceResult fenceResult) {
+ EXPECT_EQ(FenceResult(presentFence), fenceResult);
+ });
+ EXPECT_CALL(*releasedLayer2, setReleaseFence(_))
+ .WillOnce([&presentFence](FenceResult fenceResult) {
+ EXPECT_EQ(FenceResult(presentFence), fenceResult);
+ });
+ EXPECT_CALL(*releasedLayer3, setReleaseFence(_))
+ .WillOnce([&presentFence](FenceResult fenceResult) {
+ EXPECT_EQ(FenceResult(presentFence), fenceResult);
+ });
+
+ mOutput.presentFrameAndReleaseLayers();
+
+ // After the call the list of released layers should have been cleared.
+ EXPECT_TRUE(mOutput.getReleasedLayersForTest().empty());
+}
+
/*
* Output::composeSurfaces()
*/