summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/ui/include/ui/DisplayMap.h11
-rw-r--r--services/surfaceflinger/CompositionEngine/Android.bp11
-rw-r--r--services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h12
-rw-r--r--services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h1
-rw-r--r--services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h9
-rw-r--r--services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h5
-rw-r--r--services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp58
-rw-r--r--services/surfaceflinger/CompositionEngine/src/Display.cpp9
-rw-r--r--services/surfaceflinger/CompositionEngine/src/Output.cpp44
-rw-r--r--services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp18
-rw-r--r--services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp51
-rw-r--r--services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp7
12 files changed, 212 insertions, 24 deletions
diff --git a/libs/ui/include/ui/DisplayMap.h b/libs/ui/include/ui/DisplayMap.h
index 7eacb0a7f0..65d2b8fa31 100644
--- a/libs/ui/include/ui/DisplayMap.h
+++ b/libs/ui/include/ui/DisplayMap.h
@@ -23,13 +23,18 @@ namespace android::ui {
// The static capacities were chosen to exceed a typical number of physical and/or virtual displays.
+constexpr size_t kDisplayCapacity = 5;
template <typename Key, typename Value>
-using DisplayMap = ftl::SmallMap<Key, Value, 5>;
+using DisplayMap = ftl::SmallMap<Key, Value, kDisplayCapacity>;
+constexpr size_t kPhysicalDisplayCapacity = 3;
template <typename Key, typename Value>
-using PhysicalDisplayMap = ftl::SmallMap<Key, Value, 3>;
+using PhysicalDisplayMap = ftl::SmallMap<Key, Value, kPhysicalDisplayCapacity>;
template <typename T>
-using PhysicalDisplayVector = ftl::SmallVector<T, 3>;
+using DisplayVector = ftl::SmallVector<T, kDisplayCapacity>;
+
+template <typename T>
+using PhysicalDisplayVector = ftl::SmallVector<T, kPhysicalDisplayCapacity>;
} // namespace android::ui
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 2740a979f3..455e623c37 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -91,6 +91,9 @@ cc_library {
],
local_include_dirs: ["include"],
export_include_dirs: ["include"],
+ shared_libs: [
+ "server_configurable_flags",
+ ],
}
cc_library {
@@ -114,6 +117,9 @@ cc_library {
"libsurfaceflinger_common_test",
"libsurfaceflingerflags_test",
],
+ shared_libs: [
+ "server_configurable_flags",
+ ],
local_include_dirs: ["include"],
export_include_dirs: ["include"],
}
@@ -150,10 +156,11 @@ cc_test {
"libsurfaceflinger_common_test",
"libsurfaceflingerflags_test",
],
- // For some reason, libvulkan isn't picked up from librenderengine
- // Probably ASAN related?
shared_libs: [
+ // For some reason, libvulkan isn't picked up from librenderengine
+ // Probably ASAN related?
"libvulkan",
+ "server_configurable_flags",
],
sanitize: {
hwaddress: true,
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 422a79978e..f1d6f52eb8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -26,6 +26,7 @@
#include <vector>
#include <compositionengine/LayerFE.h>
+#include <ftl/future.h>
#include <renderengine/LayerSettings.h>
#include <ui/Fence.h>
#include <ui/FenceTime.h>
@@ -263,8 +264,15 @@ public:
// Prepare the output, updating the OutputLayers used in the output
virtual void prepare(const CompositionRefreshArgs&, LayerFESet&) = 0;
- // Presents the output, finalizing all composition details
- virtual void present(const CompositionRefreshArgs&) = 0;
+ // Presents the output, finalizing all composition details. This may happen
+ // asynchronously, in which case the returned future must be waited upon.
+ virtual ftl::Future<std::monostate> present(const CompositionRefreshArgs&) = 0;
+
+ // Whether this output can be presented from another thread.
+ virtual bool supportsOffloadPresent() const = 0;
+
+ // Make the next call to `present` run asynchronously.
+ virtual void offloadPresentNextFrame() = 0;
// Enables predicting composition strategy to run client composition earlier
virtual void setPredictCompositionStrategy(bool) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index de8293151b..eac5d97df3 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -62,6 +62,7 @@ public:
compositionengine::Output::FrameFences presentFrame() override;
void setExpensiveRenderingExpected(bool) override;
void finishFrame(GpuCompositionResult&&) override;
+ bool supportsOffloadPresent() const override;
// compositionengine::Display overrides
DisplayId getId() const override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index d95fbeab92..ec6a4e9c63 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -80,7 +80,9 @@ public:
void setReleasedLayers(ReleasedLayers&&) override;
void prepare(const CompositionRefreshArgs&, LayerFESet&) override;
- void present(const CompositionRefreshArgs&) override;
+ ftl::Future<std::monostate> present(const CompositionRefreshArgs&) override;
+ bool supportsOffloadPresent() const override { return false; }
+ void offloadPresentNextFrame() override;
void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) override;
void rebuildLayerStacks(const CompositionRefreshArgs&, LayerFESet&) override;
@@ -121,6 +123,7 @@ public:
virtual std::future<bool> chooseCompositionStrategyAsync(
std::optional<android::HWComposer::DeviceRequestedChanges>*);
virtual void resetCompositionStrategy();
+ virtual ftl::Future<std::monostate> presentFrameAndReleaseLayersAsync();
protected:
std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(const sp<LayerFE>&) const;
@@ -164,6 +167,7 @@ private:
ui::Dataspace getBestDataspace(ui::Dataspace*, bool*) const;
compositionengine::Output::ColorProfile pickColorProfile(
const compositionengine::CompositionRefreshArgs&) const;
+ void updateHwcAsyncWorker();
std::string mName;
std::string mNamePlusId;
@@ -177,6 +181,9 @@ private:
std::unique_ptr<planner::Planner> mPlanner;
std::unique_ptr<HwcAsyncWorker> mHwComposerAsyncWorker;
+ bool mPredictCompositionStrategy = false;
+ bool mOffloadPresent = false;
+
// Whether the content must be recomposed this frame.
bool mMustRecompose = false;
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index c88fbd6d0f..95ea3a4ed7 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -80,7 +80,10 @@ public:
MOCK_METHOD1(setReleasedLayers, void(ReleasedLayers&&));
MOCK_METHOD2(prepare, void(const compositionengine::CompositionRefreshArgs&, LayerFESet&));
- MOCK_METHOD1(present, void(const compositionengine::CompositionRefreshArgs&));
+ MOCK_METHOD1(present,
+ ftl::Future<std::monostate>(const compositionengine::CompositionRefreshArgs&));
+ MOCK_CONST_METHOD0(supportsOffloadPresent, bool());
+ MOCK_METHOD(void, offloadPresentNextFrame, ());
MOCK_METHOD1(uncacheBuffers, void(const std::vector<uint64_t>&));
MOCK_METHOD2(rebuildLayerStacks,
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
index 002177b572..748d87bdc3 100644
--- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -20,6 +20,7 @@
#include <compositionengine/OutputLayer.h>
#include <compositionengine/impl/CompositionEngine.h>
#include <compositionengine/impl/Display.h>
+#include <ui/DisplayMap.h>
#include <renderengine/RenderEngine.h>
#include <utils/Trace.h>
@@ -88,6 +89,33 @@ nsecs_t CompositionEngine::getLastFrameRefreshTimestamp() const {
return mRefreshStartTime;
}
+namespace {
+int numDisplaysWithOffloadPresentSupport(const CompositionRefreshArgs& args) {
+ if (!FlagManager::getInstance().multithreaded_present() || args.outputs.size() < 2) {
+ return 0;
+ }
+
+ int numEligibleDisplays = 0;
+ // Only run present in multiple threads if all HWC-enabled displays
+ // being refreshed support it.
+ if (!std::all_of(args.outputs.begin(), args.outputs.end(),
+ [&numEligibleDisplays](const auto& output) {
+ if (!ftl::Optional(output->getDisplayId())
+ .and_then(HalDisplayId::tryCast)) {
+ // Not HWC-enabled, so it is always
+ // client-composited.
+ return true;
+ }
+ const bool support = output->supportsOffloadPresent();
+ numEligibleDisplays += static_cast<int>(support);
+ return support;
+ })) {
+ return 0;
+ }
+ return numEligibleDisplays;
+}
+} // namespace
+
void CompositionEngine::present(CompositionRefreshArgs& args) {
ATRACE_CALL();
ALOGV(__FUNCTION__);
@@ -105,8 +133,36 @@ void CompositionEngine::present(CompositionRefreshArgs& args) {
}
}
+ // Offloading the HWC call for `present` allows us to simultaneously call it
+ // on multiple displays. This is desirable because these calls block and can
+ // be slow.
+ if (const int numEligibleDisplays = numDisplaysWithOffloadPresentSupport(args);
+ numEligibleDisplays > 1) {
+ // Leave the last eligible display on the main thread, which will
+ // allow it to run concurrently without an extra thread hop.
+ int numToOffload = numEligibleDisplays - 1;
+ for (auto& output : args.outputs) {
+ if (output->supportsOffloadPresent()) {
+ output->offloadPresentNextFrame();
+ if (--numToOffload == 0) {
+ break;
+ }
+ }
+ }
+ }
+
+ ui::DisplayVector<ftl::Future<std::monostate>> presentFutures;
for (const auto& output : args.outputs) {
- output->present(args);
+ presentFutures.push_back(output->present(args));
+ }
+
+ {
+ ATRACE_NAME("Waiting on HWC");
+ for (auto& future : presentFutures) {
+ // TODO(b/185536303): Call ftl::Future::wait() once it exists, since
+ // we do not need the return value of get().
+ future.get();
+ }
}
}
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 469fb386fc..0475881bae 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -430,4 +430,13 @@ void Display::finishFrame(GpuCompositionResult&& result) {
impl::Output::finishFrame(std::move(result));
}
+bool Display::supportsOffloadPresent() const {
+ if (const auto halDisplayId = HalDisplayId::tryCast(mId)) {
+ const auto& hwc = getCompositionEngine().getHwComposer();
+ return hwc.hasDisplayCapability(*halDisplayId, DisplayCapability::MULTI_THREADED_PRESENT);
+ }
+
+ return false;
+}
+
} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 2ae80de42a..e4d757810a 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -427,7 +427,8 @@ void Output::prepare(const compositionengine::CompositionRefreshArgs& refreshArg
uncacheBuffers(refreshArgs.bufferIdsToUncache);
}
-void Output::present(const compositionengine::CompositionRefreshArgs& refreshArgs) {
+ftl::Future<std::monostate> Output::present(
+ const compositionengine::CompositionRefreshArgs& refreshArgs) {
ATRACE_FORMAT("%s for %s", __func__, mNamePlusId.c_str());
ALOGV(__FUNCTION__);
@@ -448,8 +449,26 @@ void Output::present(const compositionengine::CompositionRefreshArgs& refreshArg
devOptRepaintFlash(refreshArgs);
finishFrame(std::move(result));
- presentFrameAndReleaseLayers();
+ ftl::Future<std::monostate> future;
+ if (mOffloadPresent) {
+ future = presentFrameAndReleaseLayersAsync();
+
+ // Only offload for this frame. The next frame will determine whether it
+ // needs to be offloaded. Leave the HwcAsyncWorker in place. For one thing,
+ // it is currently presenting. Further, it may be needed next frame, and
+ // we don't want to churn.
+ mOffloadPresent = false;
+ } else {
+ presentFrameAndReleaseLayers();
+ future = ftl::yield<std::monostate>({});
+ }
renderCachedSets(refreshArgs);
+ return future;
+}
+
+void Output::offloadPresentNextFrame() {
+ mOffloadPresent = true;
+ updateHwcAsyncWorker();
}
void Output::uncacheBuffers(std::vector<uint64_t> const& bufferIdsToUncache) {
@@ -1084,6 +1103,14 @@ void Output::prepareFrame() {
finishPrepareFrame();
}
+ftl::Future<std::monostate> Output::presentFrameAndReleaseLayersAsync() {
+ return ftl::Future<bool>(std::move(mHwComposerAsyncWorker->send([&]() {
+ presentFrameAndReleaseLayers();
+ return true;
+ })))
+ .then([](bool) { return std::monostate{}; });
+}
+
std::future<bool> Output::chooseCompositionStrategyAsync(
std::optional<android::HWComposer::DeviceRequestedChanges>* changes) {
return mHwComposerAsyncWorker->send(
@@ -1600,8 +1627,15 @@ compositionengine::Output::FrameFences Output::presentFrame() {
}
void Output::setPredictCompositionStrategy(bool predict) {
- if (predict) {
- mHwComposerAsyncWorker = std::make_unique<HwcAsyncWorker>();
+ mPredictCompositionStrategy = predict;
+ updateHwcAsyncWorker();
+}
+
+void Output::updateHwcAsyncWorker() {
+ if (mPredictCompositionStrategy || mOffloadPresent) {
+ if (!mHwComposerAsyncWorker) {
+ mHwComposerAsyncWorker = std::make_unique<HwcAsyncWorker>();
+ }
} else {
mHwComposerAsyncWorker.reset(nullptr);
}
@@ -1616,7 +1650,7 @@ bool Output::canPredictCompositionStrategy(const CompositionRefreshArgs& refresh
uint64_t outputLayerHash = getState().outputLayerHash;
editState().lastOutputLayerHash = outputLayerHash;
- if (!getState().isEnabled || !mHwComposerAsyncWorker) {
+ if (!getState().isEnabled || !mPredictCompositionStrategy) {
ALOGV("canPredictCompositionStrategy disabled");
return false;
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
index 60ed660c7a..a451ab2b77 100644
--- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
@@ -20,12 +20,15 @@
#include <compositionengine/mock/LayerFE.h>
#include <compositionengine/mock/Output.h>
#include <compositionengine/mock/OutputLayer.h>
+#include <ftl/future.h>
#include <gtest/gtest.h>
#include <renderengine/mock/RenderEngine.h>
#include "MockHWComposer.h"
#include "TimeStats/TimeStats.h"
+#include <variant>
+
namespace android::compositionengine {
namespace {
@@ -107,10 +110,19 @@ TEST_F(CompositionEnginePresentTest, worksAsExpected) {
EXPECT_CALL(*mOutput2, prepare(Ref(mRefreshArgs), _));
EXPECT_CALL(*mOutput3, prepare(Ref(mRefreshArgs), _));
+ if (FlagManager::getInstance().multithreaded_present()) {
+ EXPECT_CALL(*mOutput1, getDisplayId()).WillOnce(Return(std::nullopt));
+ EXPECT_CALL(*mOutput2, getDisplayId()).WillOnce(Return(std::nullopt));
+ EXPECT_CALL(*mOutput3, getDisplayId()).WillOnce(Return(std::nullopt));
+ }
+
// The last step is to actually present each output.
- EXPECT_CALL(*mOutput1, present(Ref(mRefreshArgs)));
- EXPECT_CALL(*mOutput2, present(Ref(mRefreshArgs)));
- EXPECT_CALL(*mOutput3, present(Ref(mRefreshArgs)));
+ EXPECT_CALL(*mOutput1, present(Ref(mRefreshArgs)))
+ .WillOnce(Return(ftl::yield<std::monostate>({})));
+ EXPECT_CALL(*mOutput2, present(Ref(mRefreshArgs)))
+ .WillOnce(Return(ftl::yield<std::monostate>({})));
+ EXPECT_CALL(*mOutput3, present(Ref(mRefreshArgs)))
+ .WillOnce(Return(ftl::yield<std::monostate>({})));
mRefreshArgs.outputs = {mOutput1, mOutput2, mOutput3};
mEngine.present(mRefreshArgs);
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 5537fcdcb5..5006e7d94a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -35,6 +35,7 @@
#include <cmath>
#include <cstdint>
+#include <variant>
#include "CallOrderStateMachineHelper.h"
#include "MockHWC2.h"
@@ -54,6 +55,7 @@ using testing::InSequence;
using testing::Invoke;
using testing::IsEmpty;
using testing::Mock;
+using testing::NiceMock;
using testing::Pointee;
using testing::Property;
using testing::Ref;
@@ -4900,5 +4902,54 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
EXPECT_EQ(mLayers[2].mLayerSettings, requests[0]);
}
+struct OutputPresentFrameAndReleaseLayersAsyncTest : public ::testing::Test {
+ // Piggy-back on OutputPrepareFrameAsyncTest's version to avoid some boilerplate.
+ struct OutputPartialMock : public OutputPrepareFrameAsyncTest::OutputPartialMock {
+ // Set up the helper functions called by the function under test to use
+ // mock implementations.
+ MOCK_METHOD0(presentFrameAndReleaseLayers, void());
+ MOCK_METHOD0(presentFrameAndReleaseLayersAsync, ftl::Future<std::monostate>());
+ };
+ OutputPresentFrameAndReleaseLayersAsyncTest() {
+ mOutput->setDisplayColorProfileForTest(
+ std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+ mOutput->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+ mOutput->setCompositionEnabled(true);
+ mRefreshArgs.outputs = {mOutput};
+ }
+
+ mock::DisplayColorProfile* mDisplayColorProfile = new NiceMock<mock::DisplayColorProfile>();
+ mock::RenderSurface* mRenderSurface = new NiceMock<mock::RenderSurface>();
+ std::shared_ptr<OutputPartialMock> mOutput{std::make_shared<NiceMock<OutputPartialMock>>()};
+ CompositionRefreshArgs mRefreshArgs;
+};
+
+TEST_F(OutputPresentFrameAndReleaseLayersAsyncTest, notCalledWhenNotRequested) {
+ EXPECT_CALL(*mOutput, presentFrameAndReleaseLayersAsync()).Times(0);
+ EXPECT_CALL(*mOutput, presentFrameAndReleaseLayers()).Times(1);
+
+ mOutput->present(mRefreshArgs);
+}
+
+TEST_F(OutputPresentFrameAndReleaseLayersAsyncTest, calledWhenRequested) {
+ EXPECT_CALL(*mOutput, presentFrameAndReleaseLayersAsync())
+ .WillOnce(Return(ftl::yield<std::monostate>({})));
+ EXPECT_CALL(*mOutput, presentFrameAndReleaseLayers()).Times(0);
+
+ mOutput->offloadPresentNextFrame();
+ mOutput->present(mRefreshArgs);
+}
+
+TEST_F(OutputPresentFrameAndReleaseLayersAsyncTest, calledForOneFrame) {
+ ::testing::InSequence inseq;
+ EXPECT_CALL(*mOutput, presentFrameAndReleaseLayersAsync())
+ .WillOnce(Return(ftl::yield<std::monostate>({})));
+ EXPECT_CALL(*mOutput, presentFrameAndReleaseLayers()).Times(1);
+
+ mOutput->offloadPresentNextFrame();
+ mOutput->present(mRefreshArgs);
+ mOutput->present(mRefreshArgs);
+}
+
} // namespace
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 2d957e6334..cc2f6c7cb3 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -1575,8 +1575,7 @@ void AidlComposer::onHotplugDisconnect(Display display) {
}
bool AidlComposer::hasMultiThreadedPresentSupport(Display display) {
-#if 0
- // TODO (b/259132483): Reenable
+ if (!FlagManager::getInstance().multithreaded_present()) return false;
const auto displayId = translate<int64_t>(display);
std::vector<AidlDisplayCapability> capabilities;
const auto status = mAidlComposerClient->getDisplayCapabilities(displayId, &capabilities);
@@ -1586,10 +1585,6 @@ bool AidlComposer::hasMultiThreadedPresentSupport(Display display) {
}
return std::find(capabilities.begin(), capabilities.end(),
AidlDisplayCapability::MULTI_THREADED_PRESENT) != capabilities.end();
-#else
- (void) display;
- return false;
-#endif
}
void AidlComposer::addReader(Display display) {