Loading services/surfaceflinger/CompositionEngine/Android.bp +4 −0 Original line number Diff line number Diff line Loading @@ -127,6 +127,10 @@ cc_library { cc_test { name: "libcompositionengine_test", test_suites: ["device-tests"], include_dirs: [ "frameworks/native/services/surfaceflinger/common/include", "frameworks/native/services/surfaceflinger/tests/unittests", ], defaults: ["libcompositionengine_defaults"], srcs: [ "tests/planner/CachedSetTest.cpp", Loading services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp +29 −34 Original line number Diff line number Diff line Loading @@ -90,29 +90,37 @@ nsecs_t CompositionEngine::getLastFrameRefreshTimestamp() const { } namespace { int numDisplaysWithOffloadPresentSupport(const CompositionRefreshArgs& args) { if (!FlagManager::getInstance().multithreaded_present() || args.outputs.size() < 2) { return 0; void offloadOutputs(Outputs& outputs) { if (!FlagManager::getInstance().multithreaded_present() || outputs.size() < 2) { return; } ui::PhysicalDisplayVector<compositionengine::Output*> outputsToOffload; for (const auto& output : outputs) { if (!ftl::Optional(output->getDisplayId()).and_then(HalDisplayId::tryCast)) { // Not HWC-enabled, so it is always client-composited. No need to offload. continue; } 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; if (!output->supportsOffloadPresent()) { return; } outputsToOffload.push_back(output.get()); } if (outputsToOffload.size() < 2) { return; } // Leave the last eligible display on the main thread, which will // allow it to run concurrently without an extra thread hop. outputsToOffload.pop_back(); for (compositionengine::Output* output : outputsToOffload) { output->offloadPresentNextFrame(); } } } // namespace Loading @@ -136,20 +144,7 @@ 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; } } } } offloadOutputs(args.outputs); ui::DisplayVector<ftl::Future<std::monostate>> presentFutures; for (const auto& output : args.outputs) { Loading services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp +166 −5 Original line number Diff line number Diff line Loading @@ -14,6 +14,8 @@ * limitations under the License. */ #include <com_android_graphics_surfaceflinger_flags.h> #include <common/test/FlagUtils.h> #include <compositionengine/CompositionRefreshArgs.h> #include <compositionengine/LayerFECompositionState.h> #include <compositionengine/impl/CompositionEngine.h> Loading @@ -29,6 +31,8 @@ #include <variant> using namespace com::android::graphics::surfaceflinger; namespace android::compositionengine { namespace { Loading Loading @@ -110,11 +114,9 @@ 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)); } // All of mOutput<i> are StrictMocks. If the flag is true, it will introduce // calls to getDisplayId, which are not relevant to this test. SET_FLAG_FOR_TEST(flags::multithreaded_present, false); // The last step is to actually present each output. EXPECT_CALL(*mOutput1, present(Ref(mRefreshArgs))) Loading Loading @@ -272,5 +274,164 @@ TEST_F(CompositionTestPreComposition, EXPECT_TRUE(mEngine.needsAnotherUpdate()); } struct CompositionEngineOffloadTest : public testing::Test { impl::CompositionEngine mEngine; CompositionRefreshArgs mRefreshArgs; std::shared_ptr<mock::Output> mDisplay1{std::make_shared<StrictMock<mock::Output>>()}; std::shared_ptr<mock::Output> mDisplay2{std::make_shared<StrictMock<mock::Output>>()}; std::shared_ptr<mock::Output> mVirtualDisplay{std::make_shared<StrictMock<mock::Output>>()}; std::shared_ptr<mock::Output> mHalVirtualDisplay{std::make_shared<StrictMock<mock::Output>>()}; static constexpr PhysicalDisplayId kDisplayId1 = PhysicalDisplayId::fromPort(123u); static constexpr PhysicalDisplayId kDisplayId2 = PhysicalDisplayId::fromPort(234u); static constexpr GpuVirtualDisplayId kGpuVirtualDisplayId{789u}; static constexpr HalVirtualDisplayId kHalVirtualDisplayId{456u}; void SetUp() override { EXPECT_CALL(*mDisplay1, getDisplayId) .WillRepeatedly(Return(std::make_optional<DisplayId>(kDisplayId1))); EXPECT_CALL(*mDisplay2, getDisplayId) .WillRepeatedly(Return(std::make_optional<DisplayId>(kDisplayId2))); EXPECT_CALL(*mVirtualDisplay, getDisplayId) .WillRepeatedly(Return(std::make_optional<DisplayId>(kGpuVirtualDisplayId))); EXPECT_CALL(*mHalVirtualDisplay, getDisplayId) .WillRepeatedly(Return(std::make_optional<DisplayId>(kHalVirtualDisplayId))); } void setOutputs(std::initializer_list<std::shared_ptr<mock::Output>> outputs) { for (auto& output : outputs) { // If we call mEngine.present, prepare and present will be called on all the // outputs in mRefreshArgs, but that's not the interesting part of the test. EXPECT_CALL(*output, prepare(Ref(mRefreshArgs), _)).Times(1); EXPECT_CALL(*output, present(Ref(mRefreshArgs))) .WillOnce(Return(ftl::yield<std::monostate>({}))); mRefreshArgs.outputs.push_back(std::move(output)); } } }; TEST_F(CompositionEngineOffloadTest, basic) { EXPECT_CALL(*mDisplay1, supportsOffloadPresent).WillOnce(Return(true)); EXPECT_CALL(*mDisplay2, supportsOffloadPresent).WillOnce(Return(true)); EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(1); EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0); SET_FLAG_FOR_TEST(flags::multithreaded_present, true); setOutputs({mDisplay1, mDisplay2}); mEngine.present(mRefreshArgs); } TEST_F(CompositionEngineOffloadTest, dependsOnSupport) { EXPECT_CALL(*mDisplay1, supportsOffloadPresent).WillOnce(Return(false)); EXPECT_CALL(*mDisplay2, supportsOffloadPresent).Times(0); EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0); EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0); SET_FLAG_FOR_TEST(flags::multithreaded_present, true); setOutputs({mDisplay1, mDisplay2}); mEngine.present(mRefreshArgs); } TEST_F(CompositionEngineOffloadTest, dependsOnSupport2) { EXPECT_CALL(*mDisplay1, supportsOffloadPresent).WillOnce(Return(true)); EXPECT_CALL(*mDisplay2, supportsOffloadPresent).WillOnce(Return(false)); EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0); EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0); SET_FLAG_FOR_TEST(flags::multithreaded_present, true); setOutputs({mDisplay1, mDisplay2}); mEngine.present(mRefreshArgs); } TEST_F(CompositionEngineOffloadTest, dependsOnFlag) { EXPECT_CALL(*mDisplay1, supportsOffloadPresent).Times(0); EXPECT_CALL(*mDisplay2, supportsOffloadPresent).Times(0); EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0); EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0); SET_FLAG_FOR_TEST(flags::multithreaded_present, false); setOutputs({mDisplay1, mDisplay2}); mEngine.present(mRefreshArgs); } TEST_F(CompositionEngineOffloadTest, oneDisplay) { EXPECT_CALL(*mDisplay1, supportsOffloadPresent).Times(0); EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0); SET_FLAG_FOR_TEST(flags::multithreaded_present, true); setOutputs({mDisplay1}); mEngine.present(mRefreshArgs); } TEST_F(CompositionEngineOffloadTest, virtualDisplay) { EXPECT_CALL(*mDisplay1, supportsOffloadPresent).WillOnce(Return(true)); EXPECT_CALL(*mDisplay2, supportsOffloadPresent).WillOnce(Return(true)); EXPECT_CALL(*mVirtualDisplay, supportsOffloadPresent).Times(0); EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(1); EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0); EXPECT_CALL(*mVirtualDisplay, offloadPresentNextFrame).Times(0); SET_FLAG_FOR_TEST(flags::multithreaded_present, true); setOutputs({mDisplay1, mDisplay2, mVirtualDisplay}); mEngine.present(mRefreshArgs); } TEST_F(CompositionEngineOffloadTest, virtualDisplay2) { EXPECT_CALL(*mDisplay1, supportsOffloadPresent).WillOnce(Return(true)); EXPECT_CALL(*mVirtualDisplay, supportsOffloadPresent).Times(0); EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0); EXPECT_CALL(*mVirtualDisplay, offloadPresentNextFrame).Times(0); SET_FLAG_FOR_TEST(flags::multithreaded_present, true); setOutputs({mDisplay1, mVirtualDisplay}); mEngine.present(mRefreshArgs); } TEST_F(CompositionEngineOffloadTest, halVirtual) { EXPECT_CALL(*mDisplay1, supportsOffloadPresent).WillOnce(Return(true)); EXPECT_CALL(*mHalVirtualDisplay, supportsOffloadPresent).WillOnce(Return(true)); EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(1); EXPECT_CALL(*mHalVirtualDisplay, offloadPresentNextFrame).Times(0); SET_FLAG_FOR_TEST(flags::multithreaded_present, true); setOutputs({mDisplay1, mHalVirtualDisplay}); mEngine.present(mRefreshArgs); } TEST_F(CompositionEngineOffloadTest, ordering) { EXPECT_CALL(*mVirtualDisplay, supportsOffloadPresent).Times(0); EXPECT_CALL(*mHalVirtualDisplay, supportsOffloadPresent).WillOnce(Return(true)); EXPECT_CALL(*mDisplay1, supportsOffloadPresent).WillOnce(Return(true)); EXPECT_CALL(*mDisplay2, supportsOffloadPresent).WillOnce(Return(true)); EXPECT_CALL(*mVirtualDisplay, offloadPresentNextFrame).Times(0); EXPECT_CALL(*mHalVirtualDisplay, offloadPresentNextFrame).Times(1); EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(1); EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0); SET_FLAG_FOR_TEST(flags::multithreaded_present, true); setOutputs({mVirtualDisplay, mHalVirtualDisplay, mDisplay1, mDisplay2}); mEngine.present(mRefreshArgs); } } // namespace } // namespace android::compositionengine Loading
services/surfaceflinger/CompositionEngine/Android.bp +4 −0 Original line number Diff line number Diff line Loading @@ -127,6 +127,10 @@ cc_library { cc_test { name: "libcompositionengine_test", test_suites: ["device-tests"], include_dirs: [ "frameworks/native/services/surfaceflinger/common/include", "frameworks/native/services/surfaceflinger/tests/unittests", ], defaults: ["libcompositionengine_defaults"], srcs: [ "tests/planner/CachedSetTest.cpp", Loading
services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp +29 −34 Original line number Diff line number Diff line Loading @@ -90,29 +90,37 @@ nsecs_t CompositionEngine::getLastFrameRefreshTimestamp() const { } namespace { int numDisplaysWithOffloadPresentSupport(const CompositionRefreshArgs& args) { if (!FlagManager::getInstance().multithreaded_present() || args.outputs.size() < 2) { return 0; void offloadOutputs(Outputs& outputs) { if (!FlagManager::getInstance().multithreaded_present() || outputs.size() < 2) { return; } ui::PhysicalDisplayVector<compositionengine::Output*> outputsToOffload; for (const auto& output : outputs) { if (!ftl::Optional(output->getDisplayId()).and_then(HalDisplayId::tryCast)) { // Not HWC-enabled, so it is always client-composited. No need to offload. continue; } 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; if (!output->supportsOffloadPresent()) { return; } outputsToOffload.push_back(output.get()); } if (outputsToOffload.size() < 2) { return; } // Leave the last eligible display on the main thread, which will // allow it to run concurrently without an extra thread hop. outputsToOffload.pop_back(); for (compositionengine::Output* output : outputsToOffload) { output->offloadPresentNextFrame(); } } } // namespace Loading @@ -136,20 +144,7 @@ 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; } } } } offloadOutputs(args.outputs); ui::DisplayVector<ftl::Future<std::monostate>> presentFutures; for (const auto& output : args.outputs) { Loading
services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp +166 −5 Original line number Diff line number Diff line Loading @@ -14,6 +14,8 @@ * limitations under the License. */ #include <com_android_graphics_surfaceflinger_flags.h> #include <common/test/FlagUtils.h> #include <compositionengine/CompositionRefreshArgs.h> #include <compositionengine/LayerFECompositionState.h> #include <compositionengine/impl/CompositionEngine.h> Loading @@ -29,6 +31,8 @@ #include <variant> using namespace com::android::graphics::surfaceflinger; namespace android::compositionengine { namespace { Loading Loading @@ -110,11 +114,9 @@ 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)); } // All of mOutput<i> are StrictMocks. If the flag is true, it will introduce // calls to getDisplayId, which are not relevant to this test. SET_FLAG_FOR_TEST(flags::multithreaded_present, false); // The last step is to actually present each output. EXPECT_CALL(*mOutput1, present(Ref(mRefreshArgs))) Loading Loading @@ -272,5 +274,164 @@ TEST_F(CompositionTestPreComposition, EXPECT_TRUE(mEngine.needsAnotherUpdate()); } struct CompositionEngineOffloadTest : public testing::Test { impl::CompositionEngine mEngine; CompositionRefreshArgs mRefreshArgs; std::shared_ptr<mock::Output> mDisplay1{std::make_shared<StrictMock<mock::Output>>()}; std::shared_ptr<mock::Output> mDisplay2{std::make_shared<StrictMock<mock::Output>>()}; std::shared_ptr<mock::Output> mVirtualDisplay{std::make_shared<StrictMock<mock::Output>>()}; std::shared_ptr<mock::Output> mHalVirtualDisplay{std::make_shared<StrictMock<mock::Output>>()}; static constexpr PhysicalDisplayId kDisplayId1 = PhysicalDisplayId::fromPort(123u); static constexpr PhysicalDisplayId kDisplayId2 = PhysicalDisplayId::fromPort(234u); static constexpr GpuVirtualDisplayId kGpuVirtualDisplayId{789u}; static constexpr HalVirtualDisplayId kHalVirtualDisplayId{456u}; void SetUp() override { EXPECT_CALL(*mDisplay1, getDisplayId) .WillRepeatedly(Return(std::make_optional<DisplayId>(kDisplayId1))); EXPECT_CALL(*mDisplay2, getDisplayId) .WillRepeatedly(Return(std::make_optional<DisplayId>(kDisplayId2))); EXPECT_CALL(*mVirtualDisplay, getDisplayId) .WillRepeatedly(Return(std::make_optional<DisplayId>(kGpuVirtualDisplayId))); EXPECT_CALL(*mHalVirtualDisplay, getDisplayId) .WillRepeatedly(Return(std::make_optional<DisplayId>(kHalVirtualDisplayId))); } void setOutputs(std::initializer_list<std::shared_ptr<mock::Output>> outputs) { for (auto& output : outputs) { // If we call mEngine.present, prepare and present will be called on all the // outputs in mRefreshArgs, but that's not the interesting part of the test. EXPECT_CALL(*output, prepare(Ref(mRefreshArgs), _)).Times(1); EXPECT_CALL(*output, present(Ref(mRefreshArgs))) .WillOnce(Return(ftl::yield<std::monostate>({}))); mRefreshArgs.outputs.push_back(std::move(output)); } } }; TEST_F(CompositionEngineOffloadTest, basic) { EXPECT_CALL(*mDisplay1, supportsOffloadPresent).WillOnce(Return(true)); EXPECT_CALL(*mDisplay2, supportsOffloadPresent).WillOnce(Return(true)); EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(1); EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0); SET_FLAG_FOR_TEST(flags::multithreaded_present, true); setOutputs({mDisplay1, mDisplay2}); mEngine.present(mRefreshArgs); } TEST_F(CompositionEngineOffloadTest, dependsOnSupport) { EXPECT_CALL(*mDisplay1, supportsOffloadPresent).WillOnce(Return(false)); EXPECT_CALL(*mDisplay2, supportsOffloadPresent).Times(0); EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0); EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0); SET_FLAG_FOR_TEST(flags::multithreaded_present, true); setOutputs({mDisplay1, mDisplay2}); mEngine.present(mRefreshArgs); } TEST_F(CompositionEngineOffloadTest, dependsOnSupport2) { EXPECT_CALL(*mDisplay1, supportsOffloadPresent).WillOnce(Return(true)); EXPECT_CALL(*mDisplay2, supportsOffloadPresent).WillOnce(Return(false)); EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0); EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0); SET_FLAG_FOR_TEST(flags::multithreaded_present, true); setOutputs({mDisplay1, mDisplay2}); mEngine.present(mRefreshArgs); } TEST_F(CompositionEngineOffloadTest, dependsOnFlag) { EXPECT_CALL(*mDisplay1, supportsOffloadPresent).Times(0); EXPECT_CALL(*mDisplay2, supportsOffloadPresent).Times(0); EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0); EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0); SET_FLAG_FOR_TEST(flags::multithreaded_present, false); setOutputs({mDisplay1, mDisplay2}); mEngine.present(mRefreshArgs); } TEST_F(CompositionEngineOffloadTest, oneDisplay) { EXPECT_CALL(*mDisplay1, supportsOffloadPresent).Times(0); EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0); SET_FLAG_FOR_TEST(flags::multithreaded_present, true); setOutputs({mDisplay1}); mEngine.present(mRefreshArgs); } TEST_F(CompositionEngineOffloadTest, virtualDisplay) { EXPECT_CALL(*mDisplay1, supportsOffloadPresent).WillOnce(Return(true)); EXPECT_CALL(*mDisplay2, supportsOffloadPresent).WillOnce(Return(true)); EXPECT_CALL(*mVirtualDisplay, supportsOffloadPresent).Times(0); EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(1); EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0); EXPECT_CALL(*mVirtualDisplay, offloadPresentNextFrame).Times(0); SET_FLAG_FOR_TEST(flags::multithreaded_present, true); setOutputs({mDisplay1, mDisplay2, mVirtualDisplay}); mEngine.present(mRefreshArgs); } TEST_F(CompositionEngineOffloadTest, virtualDisplay2) { EXPECT_CALL(*mDisplay1, supportsOffloadPresent).WillOnce(Return(true)); EXPECT_CALL(*mVirtualDisplay, supportsOffloadPresent).Times(0); EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0); EXPECT_CALL(*mVirtualDisplay, offloadPresentNextFrame).Times(0); SET_FLAG_FOR_TEST(flags::multithreaded_present, true); setOutputs({mDisplay1, mVirtualDisplay}); mEngine.present(mRefreshArgs); } TEST_F(CompositionEngineOffloadTest, halVirtual) { EXPECT_CALL(*mDisplay1, supportsOffloadPresent).WillOnce(Return(true)); EXPECT_CALL(*mHalVirtualDisplay, supportsOffloadPresent).WillOnce(Return(true)); EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(1); EXPECT_CALL(*mHalVirtualDisplay, offloadPresentNextFrame).Times(0); SET_FLAG_FOR_TEST(flags::multithreaded_present, true); setOutputs({mDisplay1, mHalVirtualDisplay}); mEngine.present(mRefreshArgs); } TEST_F(CompositionEngineOffloadTest, ordering) { EXPECT_CALL(*mVirtualDisplay, supportsOffloadPresent).Times(0); EXPECT_CALL(*mHalVirtualDisplay, supportsOffloadPresent).WillOnce(Return(true)); EXPECT_CALL(*mDisplay1, supportsOffloadPresent).WillOnce(Return(true)); EXPECT_CALL(*mDisplay2, supportsOffloadPresent).WillOnce(Return(true)); EXPECT_CALL(*mVirtualDisplay, offloadPresentNextFrame).Times(0); EXPECT_CALL(*mHalVirtualDisplay, offloadPresentNextFrame).Times(1); EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(1); EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0); SET_FLAG_FOR_TEST(flags::multithreaded_present, true); setOutputs({mVirtualDisplay, mHalVirtualDisplay, mDisplay1, mDisplay2}); mEngine.present(mRefreshArgs); } } // namespace } // namespace android::compositionengine