Offload present to a separate thread
Make Output::present() return an ftl::Future. When offloading its
HWC call, present will return a future that can be waited upon.
In CompositionEngine::present, store a vector of futures and wait
upon the results before returning. This allow's HWC's present to run in
parallel with other work; in particular, present on other displays.
Waiting here ensures that post-composition work does not start until
present has completed. Future work may defer this even later.
Reuse HwcAsyncWorker to run present on a separate thread. Add a new
variable for determining whether to run validate in parallel, since the
presence of the HwcAsyncWorker could just mean that we are offloading
present.
Read the new DisplayCapability to determine whether a display can be
offloaded to a worker thread. Only run displays in parallel if they all
have the DisplayCapability. Non HWC-enabled displays without the
capability do not prevent other displays from being offloaded. They also
run last, since they can run in parallel with HWC-enabled displays.
(The ordering is now set by SurfaceFlinger, which places physical
displays at the start of the list of outputs.)
When telling a display to offload its present call, make it only last
for a single frame. This simplifies the code while ensuring we do not
leave it enabled unnecessarily.
Leave a single present call on the main thread. This saves a thread-hop,
while still allowing it to run in parallel with other HWC work.
Only attempt to offload present if the appropriate trunk stable flag (or
debug sysprop, "debug.sf.multithreaded_present") is set.
Bug: 241285491
Bug: 259132483
Test: manual: perfetto trace
Test: libcompositionengine_test
Change-Id: Ib9d074671e32c95875ef7e0791dd95d6e595e47a
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
index 15fadbc..9041964 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 @@
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 @@
}
}
+ // 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();
+ }
}
}