diff options
41 files changed, 1075 insertions, 302 deletions
diff --git a/cmds/dumpstate/DumpstateInternal.cpp b/cmds/dumpstate/DumpstateInternal.cpp index 3091f6b5d0..6f7fea3432 100644 --- a/cmds/dumpstate/DumpstateInternal.cpp +++ b/cmds/dumpstate/DumpstateInternal.cpp @@ -162,17 +162,16 @@ int DumpFileFromFdToFd(const std::string& title, const std::string& path_string, return 0; } bool newline = false; + int poll_timeout_ms = 30 * 1000; while (true) { - uint64_t start_time = Nanotime(); pollfd fds[] = { { .fd = fd, .events = POLLIN } }; - int ret = TEMP_FAILURE_RETRY(poll(fds, arraysize(fds), 30 * 1000)); + int ret = TEMP_FAILURE_RETRY(poll(fds, arraysize(fds), poll_timeout_ms)); if (ret == -1) { dprintf(out_fd, "*** %s: poll failed: %s\n", path, strerror(errno)); newline = true; break; - } else if (ret == 0) { - uint64_t elapsed = Nanotime() - start_time; - dprintf(out_fd, "*** %s: Timed out after %.3fs\n", path, (float)elapsed / NANOS_PER_SEC); + } else if (ret == 0 && poll_timeout_ms != 0) { + dprintf(out_fd, "*** %s: Timed out after %ds\n", path, poll_timeout_ms / 1000 ); newline = true; break; } else { @@ -189,6 +188,7 @@ int DumpFileFromFdToFd(const std::string& title, const std::string& path_string, break; } } + poll_timeout_ms = 0; } if (!newline) dprintf(out_fd, "\n"); diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index dbccf30fae..aba81f60d8 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -287,18 +287,17 @@ void BLASTBufferQueue::transactionCommittedCallback(nsecs_t /*latchTime*/, // We need to check if we were waiting for a transaction callback in order to // process any pending buffers and unblock. It's possible to get transaction - // callbacks for previous requests so we need to ensure the frame from this - // transaction callback matches the last acquired buffer. Since acquireNextBuffer - // will stop processing buffers when mWaitForTransactionCallback is set, we know - // that mLastAcquiredFrameNumber is the frame we're waiting on. - // We also want to check if mNextTransaction is null because it's possible another + // callbacks for previous requests so we need to ensure that there are no pending + // frame numbers that were in a sync. We remove the frame from mSyncedFrameNumbers + // set and then check if it's empty. If there are no more pending syncs, we can + // proceed with flushing the shadow queue. + // We also want to check if mSyncTransaction is null because it's possible another // sync request came in while waiting, but it hasn't started processing yet. In that // case, we don't actually want to flush the frames in between since they will get // processed and merged with the sync transaction and released earlier than if they // were sent to SF - if (mWaitForTransactionCallback && mSyncTransaction == nullptr && - currFrameNumber >= mLastAcquiredFrameNumber) { - mWaitForTransactionCallback = false; + mSyncedFrameNumbers.erase(currFrameNumber); + if (mSyncedFrameNumbers.empty() && mSyncTransaction == nullptr) { flushShadowQueue(); } } else { @@ -416,9 +415,11 @@ void BLASTBufferQueue::releaseBufferCallback( const auto releasedBuffer = mPendingRelease.front(); mPendingRelease.pop_front(); releaseBuffer(releasedBuffer.callbackId, releasedBuffer.releaseFence); - // Don't process the transactions here if mWaitForTransactionCallback is set. Instead, let - // onFrameAvailable handle processing them since it will merge with the syncTransaction. - if (!mWaitForTransactionCallback) { + // Don't process the transactions here if mSyncedFrameNumbers is not empty. That means + // are still transactions that have sync buffers in them that have not been applied or + // dropped. Instead, let onFrameAvailable handle processing them since it will merge with + // the syncTransaction. + if (mSyncedFrameNumbers.empty()) { acquireNextBufferLocked(std::nullopt); } } @@ -442,6 +443,9 @@ void BLASTBufferQueue::releaseBuffer(const ReleaseCallbackId& callbackId, BQA_LOGV("released %s", callbackId.to_string().c_str()); mBufferItemConsumer->releaseBuffer(it->second, releaseFence); mSubmitted.erase(it); + // Remove the frame number from mSyncedFrameNumbers since we can get a release callback + // without getting a transaction committed if the buffer was dropped. + mSyncedFrameNumbers.erase(callbackId.framenumber); } void BLASTBufferQueue::acquireNextBufferLocked( @@ -608,7 +612,7 @@ void BLASTBufferQueue::acquireAndReleaseBuffer() { } void BLASTBufferQueue::flushAndWaitForFreeBuffer(std::unique_lock<std::mutex>& lock) { - if (mWaitForTransactionCallback && mNumFrameAvailable > 0) { + if (!mSyncedFrameNumbers.empty() && mNumFrameAvailable > 0) { // We are waiting on a previous sync's transaction callback so allow another sync // transaction to proceed. // @@ -635,6 +639,8 @@ void BLASTBufferQueue::flushAndWaitForFreeBuffer(std::unique_lock<std::mutex>& l void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { std::function<void(SurfaceComposerClient::Transaction*)> prevCallback = nullptr; SurfaceComposerClient::Transaction* prevTransaction = nullptr; + bool waitForTransactionCallback = !mSyncedFrameNumbers.empty(); + { BBQ_TRACE(); std::unique_lock _lock{mMutex}; @@ -666,7 +672,7 @@ void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { // add to shadow queue mNumFrameAvailable++; - if (mWaitForTransactionCallback && mNumFrameAvailable >= 2) { + if (waitForTransactionCallback && mNumFrameAvailable >= 2) { acquireAndReleaseBuffer(); } ATRACE_INT(mQueuedBufferTrace.c_str(), @@ -683,14 +689,14 @@ void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { incStrong((void*)transactionCommittedCallbackThunk); mSyncTransaction->addTransactionCommittedCallback(transactionCommittedCallbackThunk, static_cast<void*>(this)); - mWaitForTransactionCallback = true; + mSyncedFrameNumbers.emplace(item.mFrameNumber); if (mAcquireSingleBuffer) { prevCallback = mTransactionReadyCallback; prevTransaction = mSyncTransaction; mTransactionReadyCallback = nullptr; mSyncTransaction = nullptr; } - } else if (!mWaitForTransactionCallback) { + } else if (!waitForTransactionCallback) { acquireNextBufferLocked(std::nullopt); } } @@ -1097,9 +1103,9 @@ void BLASTBufferQueue::abandon() { } // Clear sync states - if (mWaitForTransactionCallback) { - BQA_LOGD("mWaitForTransactionCallback cleared"); - mWaitForTransactionCallback = false; + if (!mSyncedFrameNumbers.empty()) { + BQA_LOGD("mSyncedFrameNumbers cleared"); + mSyncedFrameNumbers.clear(); } if (mSyncTransaction != nullptr) { diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 47d801a78d..9358e29030 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -897,6 +897,10 @@ void SurfaceComposerClient::Transaction::clear() { mApplyToken = nullptr; } +uint64_t SurfaceComposerClient::Transaction::getId() { + return mId; +} + void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) { sp<ISurfaceComposer> sf(ComposerService::getComposerService()); diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 9328a54184..9d287910a5 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -251,7 +251,6 @@ private: std::queue<sp<SurfaceControl>> mSurfaceControlsWithPendingCallback GUARDED_BY(mMutex); uint32_t mCurrentMaxAcquiredBufferCount; - bool mWaitForTransactionCallback GUARDED_BY(mMutex) = false; // Flag to determine if syncTransaction should only acquire a single buffer and then clear or // continue to acquire buffers until explicitly cleared @@ -279,6 +278,8 @@ private: uint64_t mLastAppliedFrameNumber = 0; std::function<void(bool)> mTransactionHangCallback; + + std::unordered_set<uint64_t> mSyncedFrameNumbers GUARDED_BY(mMutex); }; } // namespace android diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index efbdb36fef..b598b43403 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -461,6 +461,10 @@ public: // Clears the contents of the transaction without applying it. void clear(); + // Returns the current id of the transaction. + // The id is updated every time the transaction is applied. + uint64_t getId(); + status_t apply(bool synchronous = false, bool oneWay = false); // Merge another transaction in to this one, clearing other // as if it had been applied. diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp index cb7e94c932..b993289e6a 100644 --- a/libs/gui/tests/BLASTBufferQueue_test.cpp +++ b/libs/gui/tests/BLASTBufferQueue_test.cpp @@ -161,6 +161,10 @@ public: ASSERT_EQ(numFramesSubmitted, mBlastBufferQueueAdapter->mSubmitted.size()); } + void mergeWithNextTransaction(Transaction* merge, uint64_t frameNumber) { + mBlastBufferQueueAdapter->mergeWithNextTransaction(merge, frameNumber); + } + private: sp<TestBLASTBufferQueue> mBlastBufferQueueAdapter; }; @@ -1111,6 +1115,39 @@ TEST_F(BLASTBufferQueueTest, SyncNextTransactionOverwrite) { ASSERT_TRUE(receivedCallback); } +TEST_F(BLASTBufferQueueTest, SyncNextTransactionDropBuffer) { + uint8_t r = 255; + uint8_t g = 0; + uint8_t b = 0; + + BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); + + sp<IGraphicBufferProducer> igbProducer; + setUpProducer(adapter, igbProducer); + + Transaction sync; + adapter.setSyncTransaction(sync); + queueBuffer(igbProducer, 0, 255, 0, 0); + + // Merge a transaction that has a complete callback into the next frame so we can get notified + // when to take a screenshot + CallbackHelper transactionCallback; + Transaction t; + t.addTransactionCompletedCallback(transactionCallback.function, + transactionCallback.getContext()); + adapter.mergeWithNextTransaction(&t, 2); + queueBuffer(igbProducer, r, g, b, 0); + + // Drop the buffer, but ensure the next one continues to get processed. + sync.setBuffer(mSurfaceControl, nullptr); + + CallbackData callbackData; + transactionCallback.getCallbackData(&callbackData); + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); + ASSERT_NO_FATAL_FAILURE( + checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); +} + // This test will currently fail because the old surfacecontrol will steal the last presented buffer // until the old surface control is destroyed. This is not necessarily a bug but to document a // limitation with the update API and to test any changes to make the api more robust. The current diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 13ca9ecd35..375b68433d 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -64,9 +64,10 @@ float transformAngle(const ui::Transform& transform, float angleRadians) { } bool shouldDisregardTransformation(uint32_t source) { - // Do not apply any transformations to axes from joysticks or touchpads. + // Do not apply any transformations to axes from joysticks, touchpads, or relative mice. return isFromSource(source, AINPUT_SOURCE_CLASS_JOYSTICK) || - isFromSource(source, AINPUT_SOURCE_CLASS_POSITION); + isFromSource(source, AINPUT_SOURCE_CLASS_POSITION) || + isFromSource(source, AINPUT_SOURCE_MOUSE_RELATIVE); } bool shouldDisregardOffset(uint32_t source) { diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index a92016ba3b..4b3124636b 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -715,10 +715,10 @@ TEST_F(MotionEventTest, ApplyTransform) { } TEST_F(MotionEventTest, JoystickAndTouchpadAreNotTransformed) { - constexpr static std::array kNonTransformedSources = {std::pair(AINPUT_SOURCE_TOUCHPAD, - AMOTION_EVENT_ACTION_DOWN), - std::pair(AINPUT_SOURCE_JOYSTICK, - AMOTION_EVENT_ACTION_MOVE)}; + constexpr static std::array kNonTransformedSources = + {std::pair(AINPUT_SOURCE_TOUCHPAD, AMOTION_EVENT_ACTION_DOWN), + std::pair(AINPUT_SOURCE_JOYSTICK, AMOTION_EVENT_ACTION_MOVE), + std::pair(AINPUT_SOURCE_MOUSE_RELATIVE, AMOTION_EVENT_ACTION_MOVE)}; // Create a rotate-90 transform with an offset (like a window which isn't fullscreen). ui::Transform transform(ui::Transform::ROT_90, 800, 400); transform.set(transform.tx() + 20, transform.ty() + 40); @@ -738,7 +738,7 @@ TEST_F(MotionEventTest, JoystickAndTouchpadAreNotTransformed) { TEST_F(MotionEventTest, NonPointerSourcesAreNotTranslated) { constexpr static std::array kNonPointerSources = {std::pair(AINPUT_SOURCE_TRACKBALL, AMOTION_EVENT_ACTION_DOWN), - std::pair(AINPUT_SOURCE_MOUSE_RELATIVE, + std::pair(AINPUT_SOURCE_TOUCH_NAVIGATION, AMOTION_EVENT_ACTION_MOVE)}; // Create a rotate-90 transform with an offset (like a window which isn't fullscreen). ui::Transform transform(ui::Transform::ROT_90, 800, 400); diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index a9a4c71c02..91dc61923b 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -76,7 +76,7 @@ uint32_t CursorInputMapper::getSources() const { void CursorInputMapper::populateDeviceInfo(InputDeviceInfo* info) { InputMapper::populateDeviceInfo(info); - if (mParameters.mode == Parameters::MODE_POINTER) { + if (mParameters.mode == Parameters::Mode::POINTER) { float minX, minY, maxX, maxY; if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) { info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, minX, maxX, 0.0f, 0.0f, 0.0f); @@ -131,12 +131,12 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* // Configure device mode. switch (mParameters.mode) { - case Parameters::MODE_POINTER_RELATIVE: + case Parameters::Mode::POINTER_RELATIVE: // Should not happen during first time configuration. ALOGE("Cannot start a device in MODE_POINTER_RELATIVE, starting in MODE_POINTER"); - mParameters.mode = Parameters::MODE_POINTER; + mParameters.mode = Parameters::Mode::POINTER; [[fallthrough]]; - case Parameters::MODE_POINTER: + case Parameters::Mode::POINTER: mSource = AINPUT_SOURCE_MOUSE; mXPrecision = 1.0f; mYPrecision = 1.0f; @@ -144,7 +144,7 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* mYScale = 1.0f; mPointerController = getContext()->getPointerController(getDeviceId()); break; - case Parameters::MODE_NAVIGATION: + case Parameters::Mode::NAVIGATION: mSource = AINPUT_SOURCE_TRACKBALL; mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD; mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD; @@ -157,12 +157,13 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* mHWheelScale = 1.0f; } - const bool configurePointerCapture = (!changes && config->pointerCaptureRequest.enable) || - (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE); + const bool configurePointerCapture = mParameters.mode != Parameters::Mode::NAVIGATION && + ((!changes && config->pointerCaptureRequest.enable) || + (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE)); if (configurePointerCapture) { if (config->pointerCaptureRequest.enable) { - if (mParameters.mode == Parameters::MODE_POINTER) { - mParameters.mode = Parameters::MODE_POINTER_RELATIVE; + if (mParameters.mode == Parameters::Mode::POINTER) { + mParameters.mode = Parameters::Mode::POINTER_RELATIVE; mSource = AINPUT_SOURCE_MOUSE_RELATIVE; // Keep PointerController around in order to preserve the pointer position. mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE); @@ -170,8 +171,8 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* ALOGE("Cannot request pointer capture, device is not in MODE_POINTER"); } } else { - if (mParameters.mode == Parameters::MODE_POINTER_RELATIVE) { - mParameters.mode = Parameters::MODE_POINTER; + if (mParameters.mode == Parameters::Mode::POINTER_RELATIVE) { + mParameters.mode = Parameters::Mode::POINTER; mSource = AINPUT_SOURCE_MOUSE; } else { ALOGE("Cannot release pointer capture, device is not in MODE_POINTER_RELATIVE"); @@ -186,8 +187,8 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED) || configurePointerCapture) { - if (config->pointerCaptureRequest.enable) { - // Disable any acceleration or scaling when Pointer Capture is enabled. + if (mParameters.mode == Parameters::Mode::POINTER_RELATIVE) { + // Disable any acceleration or scaling for the pointer when Pointer Capture is enabled. mPointerVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS); mWheelXVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS); mWheelYVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS); @@ -198,7 +199,8 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* } } - if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { + if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO) || + configurePointerCapture) { mOrientation = DISPLAY_ORIENTATION_0; const bool isOrientedDevice = (mParameters.orientationAware && mParameters.hasAssociatedDisplay); @@ -207,8 +209,9 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* // anything if the device is already orientation-aware. If the device is not // orientation-aware, then we need to apply the inverse rotation of the display so that // when the display rotation is applied later as a part of the per-window transform, we - // get the expected screen coordinates. - if (!isOrientedDevice) { + // get the expected screen coordinates. When pointer capture is enabled, we do not apply any + // rotations and report values directly from the input device. + if (!isOrientedDevice && mParameters.mode != Parameters::Mode::POINTER_RELATIVE) { std::optional<DisplayViewport> internalViewport = config->getDisplayViewportByType(ViewportType::INTERNAL); if (internalViewport) { @@ -221,12 +224,12 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* } void CursorInputMapper::configureParameters() { - mParameters.mode = Parameters::MODE_POINTER; + mParameters.mode = Parameters::Mode::POINTER; String8 cursorModeString; if (getDeviceContext().getConfiguration().tryGetProperty(String8("cursor.mode"), cursorModeString)) { if (cursorModeString == "navigation") { - mParameters.mode = Parameters::MODE_NAVIGATION; + mParameters.mode = Parameters::Mode::NAVIGATION; } else if (cursorModeString != "pointer" && cursorModeString != "default") { ALOGW("Invalid value for cursor.mode: '%s'", cursorModeString.string()); } @@ -237,7 +240,7 @@ void CursorInputMapper::configureParameters() { mParameters.orientationAware); mParameters.hasAssociatedDisplay = false; - if (mParameters.mode == Parameters::MODE_POINTER || mParameters.orientationAware) { + if (mParameters.mode == Parameters::Mode::POINTER || mParameters.orientationAware) { mParameters.hasAssociatedDisplay = true; } } @@ -246,21 +249,7 @@ void CursorInputMapper::dumpParameters(std::string& dump) { dump += INDENT3 "Parameters:\n"; dump += StringPrintf(INDENT4 "HasAssociatedDisplay: %s\n", toString(mParameters.hasAssociatedDisplay)); - - switch (mParameters.mode) { - case Parameters::MODE_POINTER: - dump += INDENT4 "Mode: pointer\n"; - break; - case Parameters::MODE_POINTER_RELATIVE: - dump += INDENT4 "Mode: relative pointer\n"; - break; - case Parameters::MODE_NAVIGATION: - dump += INDENT4 "Mode: navigation\n"; - break; - default: - ALOG_ASSERT(false); - } - + dump += StringPrintf(INDENT4 "Mode: %s\n", ftl::enum_string(mParameters.mode).c_str()); dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware)); } @@ -486,7 +475,7 @@ int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCod std::optional<int32_t> CursorInputMapper::getAssociatedDisplayId() { if (mParameters.hasAssociatedDisplay) { - if (mParameters.mode == Parameters::MODE_POINTER) { + if (mParameters.mode == Parameters::Mode::POINTER) { return std::make_optional(mPointerController->getDisplayId()); } else { // If the device is orientationAware and not a mouse, diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h index c84c6c4229..75aeffb846 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.h +++ b/services/inputflinger/reader/mapper/CursorInputMapper.h @@ -74,10 +74,17 @@ private: // Immutable configuration parameters. struct Parameters { - enum Mode { - MODE_POINTER, - MODE_POINTER_RELATIVE, - MODE_NAVIGATION, + enum class Mode { + // In POINTER mode, the device is a mouse that controls the mouse cursor on the screen, + // reporting absolute screen locations using SOURCE_MOUSE. + POINTER, + // A mouse device in POINTER mode switches to the POINTER_RELATIVE mode when Pointer + // Capture is enabled, and reports relative values only using SOURCE_MOUSE_RELATIVE. + POINTER_RELATIVE, + // A device in NAVIGATION mode emits relative values using SOURCE_TRACKBALL. + NAVIGATION, + + ftl_last = NAVIGATION, }; Mode mode; diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index d51ce3599a..0b8eb26a03 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -4969,6 +4969,48 @@ TEST_F(CursorInputMapperTest, PointerCaptureDisablesVelocityProcessing) { ASSERT_EQ(20, args.pointerCoords[0].getY()); } +TEST_F(CursorInputMapperTest, PointerCaptureDisablesOrientationChanges) { + addConfigurationProperty("cursor.mode", "pointer"); + CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>(); + + NotifyDeviceResetArgs resetArgs; + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); + ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime); + ASSERT_EQ(DEVICE_ID, resetArgs.deviceId); + + // Ensure the display is rotated. + prepareDisplay(DISPLAY_ORIENTATION_90); + + NotifyMotionArgs args; + + // Verify that the coordinates are rotated. + process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); + ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source); + ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action); + ASSERT_EQ(-20, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X)); + ASSERT_EQ(10, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y)); + + // Enable Pointer Capture. + mFakePolicy->setPointerCapture(true); + configureDevice(InputReaderConfiguration::CHANGE_POINTER_CAPTURE); + NotifyPointerCaptureChangedArgs captureArgs; + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyCaptureWasCalled(&captureArgs)); + ASSERT_TRUE(captureArgs.request.enable); + + // Move and verify rotation is not applied. + process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); + ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source); + ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); + ASSERT_EQ(10, args.pointerCoords[0].getX()); + ASSERT_EQ(20, args.pointerCoords[0].getY()); +} + TEST_F(CursorInputMapperTest, Process_ShouldHandleDisplayId) { CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>(); diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h index db2fd1b500..2203639b1a 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h @@ -27,6 +27,7 @@ #include <compositionengine/LayerFE.h> #include <renderengine/LayerSettings.h> #include <ui/Fence.h> +#include <ui/FenceTime.h> #include <ui/GraphicTypes.h> #include <ui/LayerStack.h> #include <ui/Region.h> @@ -311,6 +312,8 @@ protected: const Region& flashRegion, std::vector<LayerFE::LayerSettings>& clientCompositionLayers) = 0; virtual void setExpensiveRenderingExpected(bool enabled) = 0; + virtual void setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) = 0; + virtual bool isPowerHintSessionEnabled() = 0; virtual void cacheClientCompositionRequests(uint32_t cacheSize) = 0; virtual bool canPredictCompositionStrategy(const CompositionRefreshArgs&) = 0; }; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h index 61a0e6a356..fa7bc5da7f 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h @@ -89,6 +89,8 @@ public: std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(const sp<LayerFE>&) const; private: + bool isPowerHintSessionEnabled() override; + void setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) override; DisplayId mId; bool mIsDisconnected = false; Hwc2::PowerAdvisor* mPowerAdvisor = nullptr; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h index 31c51e6a8d..df721cdc89 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h @@ -140,6 +140,8 @@ protected: std::vector<LayerFE*> &outLayerFEs) override; void appendRegionFlashRequests(const Region&, std::vector<LayerFE::LayerSettings>&) override; void setExpensiveRenderingExpected(bool enabled) override; + void setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) override; + bool isPowerHintSessionEnabled() override; void dumpBase(std::string&) const; // Implemented by the final implementation for the final state it uses. diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h index cb9fbad8dd..2a04949cff 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h @@ -133,6 +133,8 @@ public: MOCK_METHOD1(canPredictCompositionStrategy, bool(const CompositionRefreshArgs&)); MOCK_METHOD1(setPredictCompositionStrategy, void(bool)); MOCK_METHOD1(setTreat170mAsSrgb, void(bool)); + MOCK_METHOD(void, setHintSessionGpuFence, (std::unique_ptr<FenceTime> && gpuFence)); + MOCK_METHOD(bool, isPowerHintSessionEnabled, ()); }; } // namespace android::compositionengine::mock diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp index b79b46b6ee..ea856e4859 100644 --- a/services/surfaceflinger/CompositionEngine/src/Display.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp @@ -243,11 +243,14 @@ bool Display::chooseCompositionStrategy( return false; } + const nsecs_t startTime = systemTime(); + // Get any composition changes requested by the HWC device, and apply them. std::optional<android::HWComposer::DeviceRequestedChanges> changes; auto& hwc = getCompositionEngine().getHwComposer(); + const bool requiresClientComposition = anyLayersRequireClientComposition(); if (status_t result = - hwc.getDeviceCompositionChanges(*halDisplayId, anyLayersRequireClientComposition(), + hwc.getDeviceCompositionChanges(*halDisplayId, requiresClientComposition, getState().earliestPresentTime, getState().previousPresentFence, getState().expectedPresentTime, outChanges); @@ -257,6 +260,11 @@ bool Display::chooseCompositionStrategy( return false; } + if (isPowerHintSessionEnabled()) { + mPowerAdvisor->setHwcValidateTiming(mId, startTime, systemTime()); + mPowerAdvisor->setRequiresClientComposition(mId, requiresClientComposition); + } + return true; } @@ -356,9 +364,24 @@ compositionengine::Output::FrameFences Display::presentAndGetFrameFences() { } auto& hwc = getCompositionEngine().getHwComposer(); + + const nsecs_t startTime = systemTime(); + + if (isPowerHintSessionEnabled()) { + if (!getCompositionEngine().getHwComposer().getComposer()->isSupported( + Hwc2::Composer::OptionalFeature::ExpectedPresentTime) && + getState().previousPresentFence->getSignalTime() != Fence::SIGNAL_TIME_PENDING) { + mPowerAdvisor->setHwcPresentDelayedTime(mId, getState().earliestPresentTime); + } + } + hwc.presentAndGetReleaseFences(*halDisplayIdOpt, getState().earliestPresentTime, getState().previousPresentFence); + if (isPowerHintSessionEnabled()) { + mPowerAdvisor->setHwcPresentTiming(mId, startTime, systemTime()); + } + fences.presentFence = hwc.getPresentFence(*halDisplayIdOpt); // TODO(b/121291683): Change HWComposer call to return entire map @@ -384,6 +407,14 @@ void Display::setExpensiveRenderingExpected(bool enabled) { } } +bool Display::isPowerHintSessionEnabled() { + return mPowerAdvisor != nullptr && mPowerAdvisor->usePowerHintSession(); +} + +void Display::setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) { + mPowerAdvisor->setGpuFenceTime(mId, std::move(gpuFence)); +} + void Display::finishFrame(const compositionengine::CompositionRefreshArgs& refreshArgs, GpuCompositionResult&& result) { // We only need to actually compose the display if: @@ -396,6 +427,13 @@ void Display::finishFrame(const compositionengine::CompositionRefreshArgs& refre } impl::Output::finishFrame(refreshArgs, std::move(result)); + + if (isPowerHintSessionEnabled()) { + auto& hwc = getCompositionEngine().getHwComposer(); + if (auto halDisplayId = HalDisplayId::tryCast(mId)) { + mPowerAdvisor->setSkippedValidate(mId, hwc.getValidateSkipped(*halDisplayId)); + } + } } } // namespace android::compositionengine::impl diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index c3385a8a8b..aa0b152db0 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -586,8 +586,29 @@ void Output::ensureOutputLayerIfVisible(sp<compositionengine::LayerFE>& layerFE, // Remove the transparent area from the visible region if (!layerFEState->isOpaque) { if (tr.preserveRects()) { - // transform the transparent region - transparentRegion = tr.transform(layerFEState->transparentRegionHint); + // Clip the transparent region to geomLayerBounds first + // The transparent region may be influenced by applications, for + // instance, by overriding ViewGroup#gatherTransparentRegion with a + // custom view. Once the layer stack -> display mapping is known, we + // must guard against very wrong inputs to prevent underflow or + // overflow errors. We do this here by constraining the transparent + // region to be within the pre-transform layer bounds, since the + // layer bounds are expected to play nicely with the full + // transform. + const Region clippedTransparentRegionHint = + layerFEState->transparentRegionHint.intersect( + Rect(layerFEState->geomLayerBounds)); + + if (clippedTransparentRegionHint.isEmpty()) { + if (!layerFEState->transparentRegionHint.isEmpty()) { + ALOGD("Layer: %s had an out of bounds transparent region", + layerFE->getDebugName()); + layerFEState->transparentRegionHint.dump("transparentRegionHint"); + } + transparentRegion.clear(); + } else { + transparentRegion = tr.transform(clippedTransparentRegionHint); + } } else { // transformation too complex, can't do the // transparent region optimization. @@ -1099,6 +1120,10 @@ void Output::finishFrame(const CompositionRefreshArgs& refreshArgs, GpuCompositi return; } + if (isPowerHintSessionEnabled()) { + // get fence end time to know when gpu is complete in display + setHintSessionGpuFence(std::make_unique<FenceTime>(new Fence(dup(optReadyFence->get())))); + } // swap buffers (presentation) mRenderSurface->queueBuffer(std::move(*optReadyFence)); } @@ -1403,6 +1428,14 @@ void Output::setExpensiveRenderingExpected(bool) { // The base class does nothing with this call. } +void Output::setHintSessionGpuFence(std::unique_ptr<FenceTime>&&) { + // The base class does nothing with this call. +} + +bool Output::isPowerHintSessionEnabled() { + return false; +} + void Output::postFramebuffer() { ATRACE_CALL(); ALOGV(__FUNCTION__); diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp index 0e5a7b6a99..344fea3331 100644 --- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp @@ -169,6 +169,7 @@ struct DisplayTestCommon : public testing::Test { EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine)); EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); + EXPECT_CALL(mPowerAdvisor, usePowerHintSession()).WillRepeatedly(Return(false)); } DisplayCreationArgs getDisplayCreationArgsForPhysicalDisplay() { diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h index 9b12b08063..d7704a893d 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h @@ -137,6 +137,7 @@ public: MOCK_METHOD(bool, hasDisplayIdleTimerCapability, (PhysicalDisplayId), (const, override)); MOCK_METHOD(Hwc2::AidlTransform, getPhysicalDisplayOrientation, (PhysicalDisplayId), (const, override)); + MOCK_METHOD(bool, getValidateSkipped, (HalDisplayId), (const, override)); }; } // namespace mock diff --git a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h index 50adcfb827..8c164edded 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h @@ -38,11 +38,32 @@ public: MOCK_METHOD(bool, usePowerHintSession, (), (override)); MOCK_METHOD(bool, supportsPowerHintSession, (), (override)); MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override)); - MOCK_METHOD(void, setTargetWorkDuration, (int64_t targetDurationNanos), (override)); - MOCK_METHOD(void, sendActualWorkDuration, (int64_t actualDurationNanos, nsecs_t timestamp), - (override)); + MOCK_METHOD(void, setTargetWorkDuration, (int64_t targetDuration), (override)); + MOCK_METHOD(void, sendActualWorkDuration, (), (override)); + MOCK_METHOD(void, sendPredictedWorkDuration, (), (override)); MOCK_METHOD(void, enablePowerHint, (bool enabled), (override)); MOCK_METHOD(bool, startPowerHintSession, (const std::vector<int32_t>& threadIds), (override)); + MOCK_METHOD(void, setGpuFenceTime, + (DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime), (override)); + MOCK_METHOD(void, setHwcValidateTiming, + (DisplayId displayId, nsecs_t valiateStartTime, nsecs_t validateEndTime), + (override)); + MOCK_METHOD(void, setHwcPresentTiming, + (DisplayId displayId, nsecs_t presentStartTime, nsecs_t presentEndTime), + (override)); + MOCK_METHOD(void, setSkippedValidate, (DisplayId displayId, bool skipped), (override)); + MOCK_METHOD(void, setRequiresClientComposition, + (DisplayId displayId, bool requiresClientComposition), (override)); + MOCK_METHOD(void, setExpectedPresentTime, (nsecs_t expectedPresentTime), (override)); + MOCK_METHOD(void, setPresentFenceTime, (nsecs_t presentFenceTime), (override)); + MOCK_METHOD(void, setHwcPresentDelayedTime, + (DisplayId displayId, + std::chrono::steady_clock::time_point earliestFrameStartTime)); + MOCK_METHOD(void, setFrameDelay, (nsecs_t frameDelayDuration), (override)); + MOCK_METHOD(void, setCommitStart, (nsecs_t commitStartTime), (override)); + MOCK_METHOD(void, setCompositeEnd, (nsecs_t compositeEndtime), (override)); + MOCK_METHOD(void, setDisplays, (std::vector<DisplayId> & displayIds), (override)); + MOCK_METHOD(void, setTotalFrameTargetWorkDuration, (int64_t targetDuration), (override)); }; } // namespace mock diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp index 063726b5e2..cf12890310 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp @@ -1505,6 +1505,8 @@ struct OutputEnsureOutputLayerIfVisibleTest : public testing::Test { static const Region kTransparentRegionHint; static const Region kTransparentRegionHintTwo; static const Region kTransparentRegionHintTwo90Rotation; + static const Region kTransparentRegionHintNegative; + static const Region kTransparentRegionHintNegativeIntersectsBounds; StrictMock<OutputPartialMock> mOutput; LayerFESet mGeomSnapshots; @@ -1528,6 +1530,10 @@ const Region OutputEnsureOutputLayerIfVisibleTest::kTransparentRegionHintTwo = Region(Rect(25, 20, 50, 75)); const Region OutputEnsureOutputLayerIfVisibleTest::kTransparentRegionHintTwo90Rotation = Region(Rect(125, 25, 180, 50)); +const Region OutputEnsureOutputLayerIfVisibleTest::kTransparentRegionHintNegative = + Region(Rect(INT32_MIN, INT32_MIN, INT32_MIN + 100, INT32_MIN + 200)); +const Region OutputEnsureOutputLayerIfVisibleTest::kTransparentRegionHintNegativeIntersectsBounds = + Region(Rect(INT32_MIN, INT32_MIN, 100, 100)); TEST_F(OutputEnsureOutputLayerIfVisibleTest, performsGeomLatchBeforeCheckingIfLayerIncluded) { EXPECT_CALL(mOutput, includesLayer(sp<LayerFE>(mLayer.layerFE))).WillOnce(Return(false)); @@ -1997,6 +2003,41 @@ TEST_F(OutputEnsureOutputLayerIfVisibleTest, blockingRegionIsInOutputSpace) { RegionEq(kTransparentRegionHintTwo90Rotation)); } +TEST_F(OutputEnsureOutputLayerIfVisibleTest, transparentRegionExcludesOutputLayer) { + mLayer.layerFEState.isOpaque = false; + mLayer.layerFEState.contentDirty = true; + mLayer.layerFEState.geomLayerBounds = kFullBoundsNoRotation.bounds().toFloatRect(); + mLayer.layerFEState.transparentRegionHint = kFullBoundsNoRotation; + + EXPECT_CALL(mOutput, ensureOutputLayer(_, _)).Times(0); +} + +TEST_F(OutputEnsureOutputLayerIfVisibleTest, transparentRegionIgnoredWhenOutsideBounds) { + mLayer.layerFEState.isOpaque = false; + mLayer.layerFEState.contentDirty = true; + mLayer.layerFEState.geomLayerBounds = kFullBoundsNoRotation.bounds().toFloatRect(); + mLayer.layerFEState.transparentRegionHint = kTransparentRegionHintNegative; + + EXPECT_CALL(mOutput, ensureOutputLayer(_, _)).Times(0); +} + +TEST_F(OutputEnsureOutputLayerIfVisibleTest, transparentRegionClipsWhenOutsideBounds) { + mLayer.layerFEState.isOpaque = false; + mLayer.layerFEState.contentDirty = true; + mLayer.layerFEState.compositionType = + aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION; + mLayer.layerFEState.transparentRegionHint = kTransparentRegionHintNegativeIntersectsBounds; + + EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u)); + EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer.layerFE))) + .WillOnce(Return(&mLayer.outputLayer)); + ensureOutputLayerIfVisible(); + + // Check that the blocking region clips an out-of-bounds transparent region. + EXPECT_THAT(mLayer.outputLayerState.outputSpaceBlockingRegionHint, + RegionEq(kTransparentRegionHint)); +} + /* * Output::present() */ @@ -3319,6 +3360,9 @@ struct OutputComposeSurfacesTest : public testing::Test { MOCK_METHOD2(appendRegionFlashRequests, void(const Region&, std::vector<LayerFE::LayerSettings>&)); MOCK_METHOD1(setExpensiveRenderingExpected, void(bool)); + MOCK_METHOD(void, setHintSessionGpuFence, (std::unique_ptr<FenceTime> && gpuFence), + (override)); + MOCK_METHOD(bool, isPowerHintSessionEnabled, (), (override)); }; OutputComposeSurfacesTest() { diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index a915b615d9..86809007b4 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -211,6 +211,7 @@ status_t DisplayDevice::initiateModeChange(const ActiveModeInfo& info, to_string(getId()).c_str()); return BAD_VALUE; } + mNumModeSwitchesInPolicy++; mUpcomingActiveMode = info; ATRACE_INT(mActiveModeFPSHwcTrace.c_str(), info.mode->getFps().getIntValue()); return mHwComposer.setActiveModeWithConstraints(getPhysicalId(), info.mode->getHwcId(), @@ -537,6 +538,27 @@ void DisplayDevice::clearDesiredActiveModeState() { mDesiredActiveModeChanged = false; } +status_t DisplayDevice::setRefreshRatePolicy( + const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, bool overridePolicy) { + const auto oldPolicy = mRefreshRateConfigs->getCurrentPolicy(); + const status_t setPolicyResult = overridePolicy + ? mRefreshRateConfigs->setOverridePolicy(policy) + : mRefreshRateConfigs->setDisplayManagerPolicy(*policy); + + if (setPolicyResult == OK) { + const int numModeChanges = mNumModeSwitchesInPolicy.exchange(0); + + ALOGI("Display %s policy changed\n" + "Previous: {%s}\n" + "Current: {%s}\n" + "%d mode changes were performed under the previous policy", + to_string(getId()).c_str(), oldPolicy.toString().c_str(), + policy ? policy->toString().c_str() : "null", numModeChanges); + } + + return setPolicyResult; +} + std::atomic<int32_t> DisplayDeviceState::sNextSequenceId(1); } // namespace android diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index d5d87b40de..2161436d44 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -249,6 +249,10 @@ public: nsecs_t getVsyncPeriodFromHWC() const; nsecs_t getRefreshTimestamp() const; + status_t setRefreshRatePolicy( + const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, + bool overridePolicy); + // release HWC resources (if any) for removable displays void disconnect(); @@ -303,6 +307,8 @@ private: TracedOrdinal<bool> mDesiredActiveModeChanged GUARDED_BY(mActiveModeLock) = {"DesiredActiveModeChanged", false}; ActiveModeInfo mUpcomingActiveMode GUARDED_BY(kMainThreadContext); + + std::atomic_int mNumModeSwitchesInPolicy = 0; }; struct DisplayDeviceState { diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index 0da8ecea85..a6aee1f2f5 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -738,6 +738,13 @@ ftl::Future<status_t> HWComposer::setDisplayBrightness( }); } +bool HWComposer::getValidateSkipped(HalDisplayId displayId) const { + if (mDisplayData.count(displayId) == 0) { + return false; + } + return mDisplayData.at(displayId).validateWasSkipped; +} + status_t HWComposer::setBootDisplayMode(PhysicalDisplayId displayId, hal::HWConfigId displayModeId) { RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index 4c0ecd8502..92a8f30f1b 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -199,6 +199,9 @@ public: PhysicalDisplayId, float brightness, float brightnessNits, const Hwc2::Composer::DisplayBrightnessOptions&) = 0; + // Get whether the display skipped validation on the latest present + virtual bool getValidateSkipped(HalDisplayId displayId) const = 0; + // Events handling --------------------------------------------------------- // Returns stable display ID (and display name on connection of new or previously disconnected @@ -397,6 +400,8 @@ public: status_t setActiveColorMode(PhysicalDisplayId, ui::ColorMode, ui::RenderIntent) override; + bool getValidateSkipped(HalDisplayId displayId) const override; + // Composer 2.4 ui::DisplayConnectionType getDisplayConnectionType(PhysicalDisplayId) const override; bool isVsyncPeriodSwitchSupported(PhysicalDisplayId) const override; diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp index b5678b49b8..77dda6c037 100644 --- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp +++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp @@ -196,7 +196,7 @@ bool PowerAdvisor::isPowerHintSessionRunning() { return mPowerHintSessionRunning; } -void PowerAdvisor::setTargetWorkDuration(int64_t targetDurationNanos) { +void PowerAdvisor::setTargetWorkDuration(int64_t targetDuration) { if (!usePowerHintSession()) { ALOGV("Power hint session target duration cannot be set, skipping"); return; @@ -205,26 +205,45 @@ void PowerAdvisor::setTargetWorkDuration(int64_t targetDurationNanos) { std::lock_guard lock(mPowerHalMutex); HalWrapper* const halWrapper = getPowerHal(); if (halWrapper != nullptr) { - halWrapper->setTargetWorkDuration(targetDurationNanos - kTargetSafetyMargin.count()); + halWrapper->setTargetWorkDuration(targetDuration); } } } -void PowerAdvisor::sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timeStampNanos) { +void PowerAdvisor::sendActualWorkDuration() { if (!mBootFinished || !usePowerHintSession()) { ALOGV("Actual work duration power hint cannot be sent, skipping"); return; } - { + const std::optional<nsecs_t> actualDuration = estimateWorkDuration(false); + if (actualDuration.has_value()) { + std::lock_guard lock(mPowerHalMutex); + HalWrapper* const halWrapper = getPowerHal(); + if (halWrapper != nullptr) { + halWrapper->sendActualWorkDuration(*actualDuration + kTargetSafetyMargin.count(), + systemTime()); + } + } +} + +void PowerAdvisor::sendPredictedWorkDuration() { + if (!mBootFinished || !usePowerHintSession()) { + ALOGV("Actual work duration power hint cannot be sent, skipping"); + return; + } + + const std::optional<nsecs_t> predictedDuration = estimateWorkDuration(true); + + if (predictedDuration.has_value()) { std::lock_guard lock(mPowerHalMutex); HalWrapper* const halWrapper = getPowerHal(); if (halWrapper != nullptr) { - halWrapper->sendActualWorkDuration(actualDurationNanos, timeStampNanos); + halWrapper->sendActualWorkDuration(*predictedDuration + kTargetSafetyMargin.count(), + systemTime()); } } } -// needs to be set after the flag is known but before PowerAdvisor enters onBootFinished void PowerAdvisor::enablePowerHint(bool enabled) { mPowerHintEnabled = enabled; } @@ -244,6 +263,301 @@ bool PowerAdvisor::startPowerHintSession(const std::vector<int32_t>& threadIds) return mPowerHintSessionRunning; } +void PowerAdvisor::setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime) { + DisplayTimingData& displayData = mDisplayTimingData[displayId]; + if (displayData.gpuEndFenceTime) { + nsecs_t signalTime = displayData.gpuEndFenceTime->getSignalTime(); + if (signalTime != Fence::SIGNAL_TIME_INVALID && signalTime != Fence::SIGNAL_TIME_PENDING) { + for (auto&& [_, otherDisplayData] : mDisplayTimingData) { + // If the previous display started before us but ended after we should have + // started, then it likely delayed our start time and we must compensate for that. + // Displays finishing earlier should have already made their way through this call + // and swapped their timing into "lastValid" from "latest", so we check that here. + if (!otherDisplayData.lastValidGpuStartTime.has_value()) continue; + if ((*otherDisplayData.lastValidGpuStartTime < *displayData.gpuStartTime) && + (*otherDisplayData.lastValidGpuEndTime > *displayData.gpuStartTime)) { + displayData.lastValidGpuStartTime = *otherDisplayData.lastValidGpuEndTime; + break; + } + } + displayData.lastValidGpuStartTime = displayData.gpuStartTime; + displayData.lastValidGpuEndTime = signalTime; + } + } + displayData.gpuEndFenceTime = std::move(fenceTime); + displayData.gpuStartTime = systemTime(); +} + +void PowerAdvisor::setHwcValidateTiming(DisplayId displayId, nsecs_t validateStartTime, + nsecs_t validateEndTime) { + DisplayTimingData& displayData = mDisplayTimingData[displayId]; + displayData.hwcValidateStartTime = validateStartTime; + displayData.hwcValidateEndTime = validateEndTime; +} + +void PowerAdvisor::setHwcPresentTiming(DisplayId displayId, nsecs_t presentStartTime, + nsecs_t presentEndTime) { + DisplayTimingData& displayData = mDisplayTimingData[displayId]; + displayData.hwcPresentStartTime = presentStartTime; + displayData.hwcPresentEndTime = presentEndTime; +} + +void PowerAdvisor::setSkippedValidate(DisplayId displayId, bool skipped) { + mDisplayTimingData[displayId].skippedValidate = skipped; +} + +void PowerAdvisor::setRequiresClientComposition(DisplayId displayId, + bool requiresClientComposition) { + mDisplayTimingData[displayId].usedClientComposition = requiresClientComposition; +} + +void PowerAdvisor::setExpectedPresentTime(nsecs_t expectedPresentTime) { + mExpectedPresentTimes.append(expectedPresentTime); +} + +void PowerAdvisor::setPresentFenceTime(nsecs_t presentFenceTime) { + mLastPresentFenceTime = presentFenceTime; +} + +void PowerAdvisor::setFrameDelay(nsecs_t frameDelayDuration) { + mFrameDelayDuration = frameDelayDuration; +} + +void PowerAdvisor::setHwcPresentDelayedTime( + DisplayId displayId, std::chrono::steady_clock::time_point earliestFrameStartTime) { + mDisplayTimingData[displayId].hwcPresentDelayedTime = + (earliestFrameStartTime - std::chrono::steady_clock::now()).count() + systemTime(); +} + +void PowerAdvisor::setCommitStart(nsecs_t commitStartTime) { + mCommitStartTimes.append(commitStartTime); +} + +void PowerAdvisor::setCompositeEnd(nsecs_t compositeEnd) { + mLastCompositeEndTime = compositeEnd; + // calculate the postcomp time here as well + std::vector<DisplayId>&& displays = getOrderedDisplayIds(&DisplayTimingData::hwcPresentEndTime); + DisplayTimingData& timingData = mDisplayTimingData[displays.back()]; + mLastPostcompDuration = compositeEnd - + (timingData.skippedValidate ? *timingData.hwcValidateEndTime + : *timingData.hwcPresentEndTime); +} + +void PowerAdvisor::setDisplays(std::vector<DisplayId>& displayIds) { + mDisplayIds = displayIds; +} + +void PowerAdvisor::setTotalFrameTargetWorkDuration(nsecs_t targetDuration) { + mTotalFrameTargetDuration = targetDuration; +} + +std::vector<DisplayId> PowerAdvisor::getOrderedDisplayIds( + std::optional<nsecs_t> DisplayTimingData::*sortBy) { + std::vector<DisplayId> sortedDisplays; + std::copy_if(mDisplayIds.begin(), mDisplayIds.end(), std::back_inserter(sortedDisplays), + [&](DisplayId id) { + return mDisplayTimingData.count(id) && + (mDisplayTimingData[id].*sortBy).has_value(); + }); + std::sort(sortedDisplays.begin(), sortedDisplays.end(), [&](DisplayId idA, DisplayId idB) { + return *(mDisplayTimingData[idA].*sortBy) < *(mDisplayTimingData[idB].*sortBy); + }); + return sortedDisplays; +} + +std::optional<nsecs_t> PowerAdvisor::estimateWorkDuration(bool earlyHint) { + if (earlyHint && (!mExpectedPresentTimes.isFull() || !mCommitStartTimes.isFull())) { + return std::nullopt; + } + + // Tracks when we finish presenting to hwc + nsecs_t estimatedEndTime = mCommitStartTimes[0]; + + // How long we spent this frame not doing anything, waiting for fences or vsync + nsecs_t idleDuration = 0; + + // Most recent previous gpu end time in the current frame, probably from a prior display, used + // as the start time for the next gpu operation if it ran over time since it probably blocked + std::optional<nsecs_t> previousValidGpuEndTime; + + // The currently estimated gpu end time for the frame, + // used to accumulate gpu time as we iterate over the active displays + std::optional<nsecs_t> estimatedGpuEndTime; + + // If we're predicting at the start of the frame, we use last frame as our reference point + // If we're predicting at the end of the frame, we use the current frame as a reference point + nsecs_t referenceFrameStartTime = (earlyHint ? mCommitStartTimes[-1] : mCommitStartTimes[0]); + + // When the prior frame should be presenting to the display + // If we're predicting at the start of the frame, we use last frame's expected present time + // If we're predicting at the end of the frame, the present fence time is already known + nsecs_t lastFramePresentTime = (earlyHint ? mExpectedPresentTimes[-1] : mLastPresentFenceTime); + + // The timing info for the previously calculated display, if there was one + std::optional<DisplayTimeline> previousDisplayReferenceTiming; + std::vector<DisplayId>&& displayIds = + getOrderedDisplayIds(&DisplayTimingData::hwcPresentStartTime); + DisplayTimeline referenceTiming, estimatedTiming; + + // Iterate over the displays in the same order they are presented + for (DisplayId displayId : displayIds) { + if (mDisplayTimingData.count(displayId) == 0) { + continue; + } + + auto& displayData = mDisplayTimingData.at(displayId); + + // mLastPresentFenceTime should always be the time of the reference frame, since it will be + // the previous frame's present fence if called at the start, and current frame's if called + // at the end + referenceTiming = displayData.calculateDisplayTimeline(mLastPresentFenceTime); + + // If this is the first display, include the duration before hwc present starts + if (!previousDisplayReferenceTiming.has_value()) { + estimatedEndTime += referenceTiming.hwcPresentStartTime - referenceFrameStartTime; + } else { // Otherwise add the time since last display's hwc present finished + estimatedEndTime += referenceTiming.hwcPresentStartTime - + previousDisplayReferenceTiming->hwcPresentEndTime; + } + + // Late hint can re-use reference timing here since it's estimating its own reference frame + estimatedTiming = earlyHint + ? referenceTiming.estimateTimelineFromReference(lastFramePresentTime, + estimatedEndTime) + : referenceTiming; + + // Update predicted present finish time with this display's present time + estimatedEndTime = estimatedTiming.hwcPresentEndTime; + + // Track how long we spent waiting for the fence, can be excluded from the timing estimate + idleDuration += estimatedTiming.probablyWaitsForPresentFence + ? lastFramePresentTime - estimatedTiming.presentFenceWaitStartTime + : 0; + + // Track how long we spent waiting to present, can be excluded from the timing estimate + idleDuration += earlyHint ? 0 : referenceTiming.hwcPresentDelayDuration; + + // Estimate the reference frame's gpu timing + auto gpuTiming = displayData.estimateGpuTiming(previousValidGpuEndTime); + if (gpuTiming.has_value()) { + previousValidGpuEndTime = gpuTiming->startTime + gpuTiming->duration; + + // Estimate the prediction frame's gpu end time from the reference frame + estimatedGpuEndTime = + std::max(estimatedTiming.hwcPresentStartTime, estimatedGpuEndTime.value_or(0)) + + gpuTiming->duration; + } + previousDisplayReferenceTiming = referenceTiming; + } + ATRACE_INT64("Idle duration", idleDuration); + + // Don't count time spent idly waiting in the estimate as we could do more work in that time + estimatedEndTime -= idleDuration; + + // We finish the frame when both present and the gpu are done, so wait for the later of the two + // Also add the frame delay duration since the target did not move while we were delayed + nsecs_t totalDuration = mFrameDelayDuration + + std::max(estimatedEndTime, estimatedGpuEndTime.value_or(0)) - mCommitStartTimes[0]; + + // We finish SurfaceFlinger when post-composition finishes, so add that in here + nsecs_t flingerDuration = estimatedEndTime + mLastPostcompDuration - mCommitStartTimes[0]; + nsecs_t combinedDuration = combineTimingEstimates(totalDuration, flingerDuration); + + return std::make_optional(combinedDuration); +} + +nsecs_t PowerAdvisor::combineTimingEstimates(nsecs_t totalDuration, nsecs_t flingerDuration) { + nsecs_t targetDuration; + { + std::lock_guard lock(mPowerHalMutex); + targetDuration = *getPowerHal()->getTargetWorkDuration(); + } + if (!mTotalFrameTargetDuration.has_value()) return flingerDuration; + + // Normalize total to the flinger target (vsync period) since that's how often we actually send + // hints + nsecs_t normalizedTotalDuration = (targetDuration * totalDuration) / *mTotalFrameTargetDuration; + return std::max(flingerDuration, normalizedTotalDuration); +} + +PowerAdvisor::DisplayTimeline PowerAdvisor::DisplayTimeline::estimateTimelineFromReference( + nsecs_t fenceTime, nsecs_t displayStartTime) { + DisplayTimeline estimated; + estimated.hwcPresentStartTime = displayStartTime; + + // We don't predict waiting for vsync alignment yet + estimated.hwcPresentDelayDuration = 0; + + // How long we expect to run before we start waiting for the fence + // For now just re-use last frame's post-present duration and assume it will not change much + // Excludes time spent waiting for vsync since that's not going to be consistent + estimated.presentFenceWaitStartTime = estimated.hwcPresentStartTime + + (presentFenceWaitStartTime - (hwcPresentStartTime + hwcPresentDelayDuration)); + estimated.probablyWaitsForPresentFence = fenceTime > estimated.presentFenceWaitStartTime; + estimated.hwcPresentEndTime = postPresentFenceHwcPresentDuration + + (estimated.probablyWaitsForPresentFence ? fenceTime + : estimated.presentFenceWaitStartTime); + return estimated; +} + +PowerAdvisor::DisplayTimeline PowerAdvisor::DisplayTimingData::calculateDisplayTimeline( + nsecs_t fenceTime) { + DisplayTimeline timeline; + // How long between calling hwc present and trying to wait on the fence + const nsecs_t fenceWaitStartDelay = + (skippedValidate ? kFenceWaitStartDelaySkippedValidate : kFenceWaitStartDelayValidated) + .count(); + + // Did our reference frame wait for an appropriate vsync before calling into hwc + const bool waitedOnHwcPresentTime = hwcPresentDelayedTime.has_value() && + *hwcPresentDelayedTime > *hwcPresentStartTime && + *hwcPresentDelayedTime < *hwcPresentEndTime; + + // Use validate start here if we skipped it because we did validate + present together + timeline.hwcPresentStartTime = skippedValidate ? *hwcValidateStartTime : *hwcPresentStartTime; + + // Use validate end here if we skipped it because we did validate + present together + timeline.hwcPresentEndTime = skippedValidate ? *hwcValidateEndTime : *hwcPresentEndTime; + + // How long hwc present was delayed waiting for the next appropriate vsync + timeline.hwcPresentDelayDuration = + (waitedOnHwcPresentTime ? *hwcPresentDelayedTime - *hwcPresentStartTime : 0); + // When we started waiting for the present fence after calling into hwc present + timeline.presentFenceWaitStartTime = + timeline.hwcPresentStartTime + timeline.hwcPresentDelayDuration + fenceWaitStartDelay; + timeline.probablyWaitsForPresentFence = fenceTime > timeline.presentFenceWaitStartTime && + fenceTime < timeline.hwcPresentEndTime; + + // How long we ran after we finished waiting for the fence but before hwc present finished + timeline.postPresentFenceHwcPresentDuration = timeline.hwcPresentEndTime - + (timeline.probablyWaitsForPresentFence ? fenceTime + : timeline.presentFenceWaitStartTime); + return timeline; +} + +std::optional<PowerAdvisor::GpuTimeline> PowerAdvisor::DisplayTimingData::estimateGpuTiming( + std::optional<nsecs_t> previousEnd) { + if (!(usedClientComposition && lastValidGpuStartTime.has_value() && gpuEndFenceTime)) { + return std::nullopt; + } + const nsecs_t latestGpuStartTime = std::max(previousEnd.value_or(0), *gpuStartTime); + const nsecs_t latestGpuEndTime = gpuEndFenceTime->getSignalTime(); + nsecs_t gpuDuration = 0; + if (latestGpuEndTime != Fence::SIGNAL_TIME_INVALID && + latestGpuEndTime != Fence::SIGNAL_TIME_PENDING) { + // If we know how long the most recent gpu duration was, use that + gpuDuration = latestGpuEndTime - latestGpuStartTime; + } else if (lastValidGpuEndTime.has_value()) { + // If we don't have the fence data, use the most recent information we do have + gpuDuration = *lastValidGpuEndTime - *lastValidGpuStartTime; + if (latestGpuEndTime == Fence::SIGNAL_TIME_PENDING) { + // If pending but went over the previous duration, use current time as the end + gpuDuration = std::max(gpuDuration, systemTime() - latestGpuStartTime); + } + } + return GpuTimeline{.duration = gpuDuration, .startTime = latestGpuStartTime}; +} + class HidlPowerHalWrapper : public PowerAdvisor::HalWrapper { public: HidlPowerHalWrapper(sp<V1_3::IPower> powerHal) : mPowerHal(std::move(powerHal)) {} @@ -325,6 +639,10 @@ AidlPowerHalWrapper::AidlPowerHalWrapper(sp<IPower> powerHal) : mPowerHal(std::m } mSupportsPowerHint = checkPowerHintSessionSupported(); + + mAllowedActualDeviation = + base::GetIntProperty<nsecs_t>("debug.sf.allowed_actual_deviation", + std::chrono::nanoseconds(250us).count()); } AidlPowerHalWrapper::~AidlPowerHalWrapper() { @@ -332,7 +650,7 @@ AidlPowerHalWrapper::~AidlPowerHalWrapper() { mPowerHintSession->close(); mPowerHintSession = nullptr; } -}; +} std::unique_ptr<PowerAdvisor::HalWrapper> AidlPowerHalWrapper::connect() { // This only waits if the service is actually declared @@ -370,7 +688,7 @@ bool AidlPowerHalWrapper::notifyDisplayUpdateImminent() { return ret.isOk(); } -// only version 2+ of the aidl supports power hint sessions, hidl has no support +// Only version 2+ of the aidl supports power hint sessions, hidl has no support bool AidlPowerHalWrapper::supportsPowerHintSession() { return mSupportsPowerHint; } @@ -424,30 +742,14 @@ bool AidlPowerHalWrapper::startPowerHintSession() { return isPowerHintSessionRunning(); } -bool AidlPowerHalWrapper::shouldSetTargetDuration(int64_t targetDurationNanos) { - if (targetDurationNanos <= 0) { - return false; - } - // report if the change in target from our last submission to now exceeds the threshold - return abs(1.0 - - static_cast<double>(mLastTargetDurationSent) / - static_cast<double>(targetDurationNanos)) >= kAllowedTargetDeviationPercent; -} - -void AidlPowerHalWrapper::setTargetWorkDuration(int64_t targetDurationNanos) { +void AidlPowerHalWrapper::setTargetWorkDuration(int64_t targetDuration) { ATRACE_CALL(); - mTargetDuration = targetDurationNanos; - if (sTraceHintSessionData) ATRACE_INT64("Time target", targetDurationNanos); - if (!sNormalizeTarget && isPowerHintSessionRunning() && - shouldSetTargetDuration(targetDurationNanos)) { - if (mLastActualDurationSent.has_value()) { - // update the error term here since we are actually sending an update to powerhal - if (sTraceHintSessionData) - ATRACE_INT64("Target error term", targetDurationNanos - *mLastActualDurationSent); - } - ALOGV("Sending target time: %" PRId64 "ns", targetDurationNanos); - mLastTargetDurationSent = targetDurationNanos; - auto ret = mPowerHintSession->updateTargetWorkDuration(targetDurationNanos); + mTargetDuration = targetDuration; + if (sTraceHintSessionData) ATRACE_INT64("Time target", targetDuration); + if (isPowerHintSessionRunning() && (targetDuration != mLastTargetDurationSent)) { + ALOGV("Sending target time: %" PRId64 "ns", targetDuration); + mLastTargetDurationSent = targetDuration; + auto ret = mPowerHintSession->updateTargetWorkDuration(targetDuration); if (!ret.isOk()) { ALOGW("Failed to set power hint target work duration with error: %s", ret.exceptionMessage().c_str()); @@ -456,8 +758,8 @@ void AidlPowerHalWrapper::setTargetWorkDuration(int64_t targetDurationNanos) { } } -bool AidlPowerHalWrapper::shouldReportActualDurationsNow() { - // report if we have never reported before or are approaching a stale session +bool AidlPowerHalWrapper::shouldReportActualDurations() { + // Report if we have never reported before or are approaching a stale session if (!mLastActualDurationSent.has_value() || (systemTime() - mLastActualReportTimestamp) > kStaleTimeout.count()) { return true; @@ -466,65 +768,42 @@ bool AidlPowerHalWrapper::shouldReportActualDurationsNow() { if (!mActualDuration.has_value()) { return false; } - - // duration of most recent timing - const double mostRecentActualDuration = static_cast<double>(*mActualDuration); - // duration of the last timing actually reported to the powerhal - const double lastReportedActualDuration = static_cast<double>(*mLastActualDurationSent); - - // report if the change in duration from then to now exceeds the threshold - return abs(1.0 - mostRecentActualDuration / lastReportedActualDuration) >= - kAllowedActualDeviationPercent; + // Report if the change in actual duration exceeds the threshold + return abs(*mActualDuration - *mLastActualDurationSent) > mAllowedActualDeviation; } -void AidlPowerHalWrapper::sendActualWorkDuration(int64_t actualDurationNanos, - nsecs_t timeStampNanos) { +void AidlPowerHalWrapper::sendActualWorkDuration(int64_t actualDuration, nsecs_t timestamp) { ATRACE_CALL(); - if (actualDurationNanos < 0 || !isPowerHintSessionRunning()) { + if (actualDuration < 0 || !isPowerHintSessionRunning()) { ALOGV("Failed to send actual work duration, skipping"); return; } - nsecs_t reportedDuration = actualDurationNanos; + const nsecs_t reportedDuration = actualDuration; - // normalize the sent values to a pre-set target - if (sNormalizeTarget) { - reportedDuration += mLastTargetDurationSent - mTargetDuration; - } else { - // when target duration change is within deviation and not updated, adjust the actual - // duration proportionally based on the difference, e.g. if new target is 5ms longer than - // last reported but actual duration is the same as last target, we want to report a smaller - // actual work duration now to indicate that we are overshooting - if (mLastTargetDurationSent != kDefaultTarget.count() && mTargetDuration != 0) { - reportedDuration = - static_cast<int64_t>(static_cast<long double>(mLastTargetDurationSent) / - mTargetDuration * actualDurationNanos); - mActualDuration = reportedDuration; - } - } mActualDuration = reportedDuration; WorkDuration duration; duration.durationNanos = reportedDuration; - duration.timeStampNanos = timeStampNanos; + duration.timeStampNanos = timestamp; mPowerHintQueue.push_back(duration); if (sTraceHintSessionData) { - ATRACE_INT64("Measured duration", actualDurationNanos); - ATRACE_INT64("Target error term", mTargetDuration - actualDurationNanos); + ATRACE_INT64("Measured duration", actualDuration); + ATRACE_INT64("Target error term", actualDuration - mTargetDuration); ATRACE_INT64("Reported duration", reportedDuration); ATRACE_INT64("Reported target", mLastTargetDurationSent); - ATRACE_INT64("Reported target error term", mLastTargetDurationSent - reportedDuration); + ATRACE_INT64("Reported target error term", reportedDuration - mLastTargetDurationSent); } ALOGV("Sending actual work duration of: %" PRId64 " on reported target: %" PRId64 " with error: %" PRId64, - reportedDuration, mLastTargetDurationSent, mLastTargetDurationSent - reportedDuration); + reportedDuration, mLastTargetDurationSent, reportedDuration - mLastTargetDurationSent); // This rate limiter queues similar duration reports to the powerhal into // batches to avoid excessive binder calls. The criteria to send a given batch // are outlined in shouldReportActualDurationsNow() - if (shouldReportActualDurationsNow()) { + if (shouldReportActualDurations()) { ALOGV("Sending hint update batch"); mLastActualReportTimestamp = systemTime(); auto ret = mPowerHintSession->reportActualWorkDuration(mPowerHintQueue); @@ -534,8 +813,8 @@ void AidlPowerHalWrapper::sendActualWorkDuration(int64_t actualDurationNanos, mShouldReconnectHal = true; } mPowerHintQueue.clear(); - // we save the non-normalized value here to detect % changes - mLastActualDurationSent = reportedDuration; + // We save the actual duration here for rate limiting + mLastActualDurationSent = actualDuration; } } @@ -551,12 +830,13 @@ std::optional<int64_t> AidlPowerHalWrapper::getTargetWorkDuration() { return mTargetDuration; } +void AidlPowerHalWrapper::setAllowedActualDeviation(nsecs_t allowedDeviation) { + mAllowedActualDeviation = allowedDeviation; +} + const bool AidlPowerHalWrapper::sTraceHintSessionData = base::GetBoolProperty(std::string("debug.sf.trace_hint_sessions"), false); -const bool AidlPowerHalWrapper::sNormalizeTarget = - base::GetBoolProperty(std::string("debug.sf.normalize_hint_session_durations"), false); - PowerAdvisor::HalWrapper* PowerAdvisor::getPowerHal() { static std::unique_ptr<HalWrapper> sHalWrapper = nullptr; static bool sHasHal = true; @@ -565,7 +845,7 @@ PowerAdvisor::HalWrapper* PowerAdvisor::getPowerHal() { return nullptr; } - // grab old hint session values before we destroy any existing wrapper + // Grab old hint session values before we destroy any existing wrapper std::vector<int32_t> oldPowerHintSessionThreadIds; std::optional<int64_t> oldTargetWorkDuration; @@ -582,7 +862,7 @@ PowerAdvisor::HalWrapper* PowerAdvisor::getPowerHal() { if (sHalWrapper != nullptr) { auto wrapper = sHalWrapper.get(); - // if the wrapper is fine, return it, but if it indicates a reconnect, remake it + // If the wrapper is fine, return it, but if it indicates a reconnect, remake it if (!wrapper->shouldReconnectHAL()) { return wrapper; } @@ -590,7 +870,7 @@ PowerAdvisor::HalWrapper* PowerAdvisor::getPowerHal() { sHalWrapper = nullptr; } - // at this point, we know for sure there is no running session + // At this point, we know for sure there is no running session mPowerHintSessionRunning = false; // First attempt to connect to the AIDL Power HAL @@ -601,13 +881,12 @@ PowerAdvisor::HalWrapper* PowerAdvisor::getPowerHal() { sHalWrapper = HidlPowerHalWrapper::connect(); } else { ALOGD("Successfully connecting AIDL Power HAL"); - // if AIDL, pass on any existing hint session values - // thread ids always safe to set + // If AIDL, pass on any existing hint session values sHalWrapper->setPowerHintSessionThreadIds(oldPowerHintSessionThreadIds); - // only set duration and start if duration is defined + // Only set duration and start if duration is defined if (oldTargetWorkDuration.has_value()) { sHalWrapper->setTargetWorkDuration(*oldTargetWorkDuration); - // only start if possible to run and both threadids and duration are defined + // Only start if possible to run and both threadids and duration are defined if (usePowerHintSession() && !oldPowerHintSessionThreadIds.empty()) { mPowerHintSessionRunning = sHalWrapper->startPowerHintSession(); } diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h index 7c10e19e11..98921b0861 100644 --- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h +++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h @@ -18,11 +18,15 @@ #include <atomic> #include <chrono> +#include <unordered_map> #include <unordered_set> +#include <ui/DisplayId.h> +#include <ui/FenceTime.h> #include <utils/Mutex.h> #include <android/hardware/power/IPower.h> +#include <compositionengine/impl/OutputCompositionState.h> #include <ui/DisplayIdentification.h> #include "../Scheduler/OneShotTimer.h" @@ -44,13 +48,50 @@ public: virtual void setExpensiveRenderingExpected(DisplayId displayId, bool expected) = 0; virtual bool isUsingExpensiveRendering() = 0; virtual void notifyDisplayUpdateImminent() = 0; + // Checks both if it supports and if it's enabled virtual bool usePowerHintSession() = 0; virtual bool supportsPowerHintSession() = 0; virtual bool isPowerHintSessionRunning() = 0; - virtual void setTargetWorkDuration(int64_t targetDurationNanos) = 0; - virtual void sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timestamp) = 0; + // Sends a power hint that updates to the target work duration for the frame + virtual void setTargetWorkDuration(nsecs_t targetDuration) = 0; + // Sends a power hint for the actual known work duration at the end of the frame + virtual void sendActualWorkDuration() = 0; + // Sends a power hint for the upcoming frame predicted from previous frame timing + virtual void sendPredictedWorkDuration() = 0; + // Sets whether the power hint session is enabled virtual void enablePowerHint(bool enabled) = 0; + // Initializes the power hint session virtual bool startPowerHintSession(const std::vector<int32_t>& threadIds) = 0; + // Provides PowerAdvisor with a copy of the gpu fence so it can determine the gpu end time + virtual void setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime) = 0; + // Reports the start and end times of a hwc validate call this frame for a given display + virtual void setHwcValidateTiming(DisplayId displayId, nsecs_t validateStartTime, + nsecs_t validateEndTime) = 0; + // Reports the start and end times of a hwc present call this frame for a given display + virtual void setHwcPresentTiming(DisplayId displayId, nsecs_t presentStartTime, + nsecs_t presentEndTime) = 0; + // Reports the expected time that the current frame will present to the display + virtual void setExpectedPresentTime(nsecs_t expectedPresentTime) = 0; + // Reports the most recent present fence time once it's known at the end of the frame + virtual void setPresentFenceTime(nsecs_t presentFenceTime) = 0; + // Reports whether a display used client composition this frame + virtual void setRequiresClientComposition(DisplayId displayId, + bool requiresClientComposition) = 0; + // Reports whether a given display skipped validation this frame + virtual void setSkippedValidate(DisplayId displayId, bool skipped) = 0; + // Reports when a hwc present is delayed, and the time that it will resume + virtual void setHwcPresentDelayedTime( + DisplayId displayId, std::chrono::steady_clock::time_point earliestFrameStartTime) = 0; + // Reports the start delay for SurfaceFlinger this frame + virtual void setFrameDelay(nsecs_t frameDelayDuration) = 0; + // Reports the SurfaceFlinger commit start time this frame + virtual void setCommitStart(nsecs_t commitStartTime) = 0; + // Reports the SurfaceFlinger composite end time this frame + virtual void setCompositeEnd(nsecs_t compositeEndTime) = 0; + // Reports the list of the currently active displays + virtual void setDisplays(std::vector<DisplayId>& displayIds) = 0; + // Sets the target duration for the entire pipeline including the gpu + virtual void setTotalFrameTargetWorkDuration(nsecs_t targetDuration) = 0; }; namespace impl { @@ -70,12 +111,11 @@ public: virtual void restartPowerHintSession() = 0; virtual void setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) = 0; virtual bool startPowerHintSession() = 0; - virtual void setTargetWorkDuration(int64_t targetDurationNanos) = 0; - virtual void sendActualWorkDuration(int64_t actualDurationNanos, - nsecs_t timeStampNanos) = 0; + virtual void setTargetWorkDuration(nsecs_t targetDuration) = 0; + virtual void sendActualWorkDuration(nsecs_t actualDuration, nsecs_t timestamp) = 0; virtual bool shouldReconnectHAL() = 0; virtual std::vector<int32_t> getPowerHintSessionThreadIds() = 0; - virtual std::optional<int64_t> getTargetWorkDuration() = 0; + virtual std::optional<nsecs_t> getTargetWorkDuration() = 0; }; PowerAdvisor(SurfaceFlinger& flinger); @@ -89,10 +129,29 @@ public: bool usePowerHintSession() override; bool supportsPowerHintSession() override; bool isPowerHintSessionRunning() override; - void setTargetWorkDuration(int64_t targetDurationNanos) override; - void sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timestamp) override; + void setTargetWorkDuration(nsecs_t targetDuration) override; + void sendActualWorkDuration() override; + void sendPredictedWorkDuration() override; void enablePowerHint(bool enabled) override; bool startPowerHintSession(const std::vector<int32_t>& threadIds) override; + void setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime); + void setHwcValidateTiming(DisplayId displayId, nsecs_t valiateStartTime, + nsecs_t validateEndTime) override; + void setHwcPresentTiming(DisplayId displayId, nsecs_t presentStartTime, + nsecs_t presentEndTime) override; + void setSkippedValidate(DisplayId displayId, bool skipped) override; + void setRequiresClientComposition(DisplayId displayId, bool requiresClientComposition) override; + void setExpectedPresentTime(nsecs_t expectedPresentTime) override; + void setPresentFenceTime(nsecs_t presentFenceTime) override; + void setHwcPresentDelayedTime( + DisplayId displayId, + std::chrono::steady_clock::time_point earliestFrameStartTime) override; + + void setFrameDelay(nsecs_t frameDelayDuration) override; + void setCommitStart(nsecs_t commitStartTime) override; + void setCompositeEnd(nsecs_t compositeEndTime) override; + void setDisplays(std::vector<DisplayId>& displayIds) override; + void setTotalFrameTargetWorkDuration(nsecs_t targetDuration) override; private: HalWrapper* getPowerHal() REQUIRES(mPowerHalMutex); @@ -100,15 +159,6 @@ private: std::mutex mPowerHalMutex; std::atomic_bool mBootFinished = false; - std::optional<bool> mPowerHintEnabled; - std::optional<bool> mSupportsPowerHint; - bool mPowerHintSessionRunning = false; - - // An adjustable safety margin which moves the "target" earlier to allow flinger to - // go a bit over without dropping a frame, especially since we can't measure - // the exact time HWC finishes composition so "actual" durations are measured - // from the end of present() instead, which is a bit later. - static constexpr const std::chrono::nanoseconds kTargetSafetyMargin = 2ms; std::unordered_set<DisplayId> mExpensiveDisplays; bool mNotifiedExpensiveRendering = false; @@ -117,6 +167,110 @@ private: std::atomic_bool mSendUpdateImminent = true; std::atomic<nsecs_t> mLastScreenUpdatedTime = 0; std::optional<scheduler::OneShotTimer> mScreenUpdateTimer; + + // Higher-level timing data used for estimation + struct DisplayTimeline { + // The start of hwc present, or the start of validate if it happened there instead + nsecs_t hwcPresentStartTime = -1; + // The end of hwc present or validate, whichever one actually presented + nsecs_t hwcPresentEndTime = -1; + // How long the actual hwc present was delayed after hwcPresentStartTime + nsecs_t hwcPresentDelayDuration = 0; + // When we think we started waiting for the present fence after calling into hwc present and + // after potentially waiting for the earliest present time + nsecs_t presentFenceWaitStartTime = -1; + // How long we ran after we finished waiting for the fence but before hwc present finished + nsecs_t postPresentFenceHwcPresentDuration = 0; + // Are we likely to have waited for the present fence during composition + bool probablyWaitsForPresentFence = false; + // Estimate one frame's timeline from that of a previous frame + DisplayTimeline estimateTimelineFromReference(nsecs_t fenceTime, nsecs_t displayStartTime); + }; + + struct GpuTimeline { + nsecs_t duration = 0; + nsecs_t startTime = -1; + }; + + // Power hint session data recorded from the pipeline + struct DisplayTimingData { + std::unique_ptr<FenceTime> gpuEndFenceTime; + std::optional<nsecs_t> gpuStartTime; + std::optional<nsecs_t> lastValidGpuEndTime; + std::optional<nsecs_t> lastValidGpuStartTime; + std::optional<nsecs_t> hwcPresentStartTime; + std::optional<nsecs_t> hwcPresentEndTime; + std::optional<nsecs_t> hwcValidateStartTime; + std::optional<nsecs_t> hwcValidateEndTime; + std::optional<nsecs_t> hwcPresentDelayedTime; + bool usedClientComposition = false; + bool skippedValidate = false; + // Calculate high-level timing milestones from more granular display timing data + DisplayTimeline calculateDisplayTimeline(nsecs_t fenceTime); + // Estimate the gpu duration for a given display from previous gpu timing data + std::optional<GpuTimeline> estimateGpuTiming(std::optional<nsecs_t> previousEnd); + }; + + template <class T, size_t N> + class RingBuffer { + std::array<T, N> elements = {}; + size_t mIndex = 0; + size_t numElements = 0; + + public: + void append(T item) { + mIndex = (mIndex + 1) % N; + numElements = std::min(N, numElements + 1); + elements[mIndex] = item; + } + bool isFull() const { return numElements == N; } + // Allows access like [0] == current, [-1] = previous, etc.. + T& operator[](int offset) { + size_t positiveOffset = + static_cast<size_t>((offset % static_cast<int>(N)) + static_cast<int>(N)); + return elements[(mIndex + positiveOffset) % N]; + } + }; + + // Filter and sort the display ids by a given property + std::vector<DisplayId> getOrderedDisplayIds(std::optional<nsecs_t> DisplayTimingData::*sortBy); + // Estimates a frame's total work duration including gpu time. + // Runs either at the beginning or end of a frame, using the most recent data available + std::optional<nsecs_t> estimateWorkDuration(bool earlyHint); + // There are two different targets and actual work durations we care about, + // this normalizes them together and takes the max of the two + nsecs_t combineTimingEstimates(nsecs_t totalDuration, nsecs_t flingerDuration); + + std::unordered_map<DisplayId, DisplayTimingData> mDisplayTimingData; + + // Current frame's delay + nsecs_t mFrameDelayDuration = 0; + // Last frame's composite end time + nsecs_t mLastCompositeEndTime = -1; + // Last frame's post-composition duration + nsecs_t mLastPostcompDuration = 0; + // Buffer of recent commit start times + RingBuffer<nsecs_t, 2> mCommitStartTimes; + // Buffer of recent expected present times + RingBuffer<nsecs_t, 2> mExpectedPresentTimes; + // Most recent present fence time, set at the end of the frame once known + nsecs_t mLastPresentFenceTime = -1; + // Target for the entire pipeline including gpu + std::optional<nsecs_t> mTotalFrameTargetDuration; + // Updated list of display IDs + std::vector<DisplayId> mDisplayIds; + + std::optional<bool> mPowerHintEnabled; + std::optional<bool> mSupportsPowerHint; + bool mPowerHintSessionRunning = false; + + // An adjustable safety margin which pads the "actual" value sent to PowerHAL, + // encouraging more aggressive boosting to give SurfaceFlinger a larger margin for error + static constexpr const std::chrono::nanoseconds kTargetSafetyMargin = 1ms; + + // How long we expect hwc to run after the present call until it waits for the fence + static constexpr const std::chrono::nanoseconds kFenceWaitStartDelayValidated = 150us; + static constexpr const std::chrono::nanoseconds kFenceWaitStartDelaySkippedValidate = 250us; }; class AidlPowerHalWrapper : public PowerAdvisor::HalWrapper { @@ -133,50 +287,50 @@ public: void restartPowerHintSession() override; void setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) override; bool startPowerHintSession() override; - void setTargetWorkDuration(int64_t targetDurationNanos) override; - void sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timeStampNanos) override; + void setTargetWorkDuration(nsecs_t targetDuration) override; + void sendActualWorkDuration(nsecs_t actualDuration, nsecs_t timestamp) override; bool shouldReconnectHAL() override; std::vector<int32_t> getPowerHintSessionThreadIds() override; - std::optional<int64_t> getTargetWorkDuration() override; + std::optional<nsecs_t> getTargetWorkDuration() override; private: + friend class AidlPowerHalWrapperTest; + bool checkPowerHintSessionSupported(); void closePowerHintSession(); - bool shouldReportActualDurationsNow(); - bool shouldSetTargetDuration(int64_t targetDurationNanos); + bool shouldReportActualDurations(); + + // Used for testing + void setAllowedActualDeviation(nsecs_t); const sp<hardware::power::IPower> mPowerHal = nullptr; bool mHasExpensiveRendering = false; bool mHasDisplayUpdateImminent = false; // Used to indicate an error state and need for reconstruction bool mShouldReconnectHal = false; - // This is not thread safe, but is currently protected by mPowerHalMutex so it needs no lock + + // Power hint session data + + // Concurrent access for this is protected by mPowerHalMutex sp<hardware::power::IPowerHintSession> mPowerHintSession = nullptr; // Queue of actual durations saved to report std::vector<hardware::power::WorkDuration> mPowerHintQueue; - // The latest un-normalized values we have received for target and actual - int64_t mTargetDuration = kDefaultTarget.count(); - std::optional<int64_t> mActualDuration; + // The latest values we have received for target and actual + nsecs_t mTargetDuration = kDefaultTarget.count(); + std::optional<nsecs_t> mActualDuration; // The list of thread ids, stored so we can restart the session from this class if needed std::vector<int32_t> mPowerHintThreadIds; - bool mSupportsPowerHint; + bool mSupportsPowerHint = false; // Keep track of the last messages sent for rate limiter change detection - std::optional<int64_t> mLastActualDurationSent; - // timestamp of the last report we sent, used to avoid stale sessions - int64_t mLastActualReportTimestamp = 0; - int64_t mLastTargetDurationSent = kDefaultTarget.count(); - // Whether to normalize all the actual values as error terms relative to a constant target - // This saves a binder call by not setting the target, and should not affect the pid values - static const bool sNormalizeTarget; + std::optional<nsecs_t> mLastActualDurationSent; + // Timestamp of the last report we sent, used to avoid stale sessions + nsecs_t mLastActualReportTimestamp = 0; + nsecs_t mLastTargetDurationSent = kDefaultTarget.count(); + // Max amount the error term can vary without causing an actual value report + nsecs_t mAllowedActualDeviation = -1; // Whether we should emit ATRACE_INT data for hint sessions static const bool sTraceHintSessionData; - - // Max percent the actual duration can vary without causing a report (eg: 0.1 = 10%) - static constexpr double kAllowedActualDeviationPercent = 0.1; - // Max percent the target duration can vary without causing a report (eg: 0.1 = 10%) - static constexpr double kAllowedTargetDeviationPercent = 0.1; - // Target used for init and normalization, the actual value does not really matter - static constexpr const std::chrono::nanoseconds kDefaultTarget = 50ms; + static constexpr const std::chrono::nanoseconds kDefaultTarget = 16ms; // Amount of time after the last message was sent before the session goes stale // actually 100ms but we use 80 here to ideally avoid going stale static constexpr const std::chrono::nanoseconds kStaleTimeout = 80ms; diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 37f0fec425..727cb0817e 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -25,6 +25,7 @@ #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h> #include <configstore/Utils.h> +#include <ftl/fake_guard.h> #include <gui/WindowInfo.h> #include <system/window.h> #include <ui/DisplayStatInfo.h> @@ -94,9 +95,13 @@ void Scheduler::startTimers() { } void Scheduler::setRefreshRateConfigs(std::shared_ptr<RefreshRateConfigs> configs) { + // The current RefreshRateConfigs instance may outlive this call, so unbind its idle timer. { - // The current RefreshRateConfigs instance may outlive this call, so unbind its idle timer. - std::scoped_lock lock(mRefreshRateConfigsLock); + // mRefreshRateConfigsLock is not locked here to avoid the deadlock + // as the callback can attempt to acquire the lock before stopIdleTimer can finish + // the execution. It's safe to FakeGuard as main thread is the only thread that + // writes to the mRefreshRateConfigs. + ftl::FakeGuard guard(mRefreshRateConfigsLock); if (mRefreshRateConfigs) { mRefreshRateConfigs->stopIdleTimer(); mRefreshRateConfigs->clearIdleTimerCallbacks(); @@ -554,11 +559,12 @@ void Scheduler::onTouchHint() { } } -void Scheduler::setDisplayPowerState(bool normal) { +void Scheduler::setDisplayPowerMode(hal::PowerMode powerMode) { { std::lock_guard<std::mutex> lock(mPolicyLock); - mPolicy.isDisplayPowerStateNormal = normal; + mPolicy.displayPowerMode = powerMode; } + mVsyncSchedule->getController().setDisplayPowerMode(powerMode); if (mDisplayPowerTimer) { mDisplayPowerTimer->reset(); @@ -706,7 +712,8 @@ auto Scheduler::chooseDisplayMode() -> std::pair<DisplayModePtr, GlobalSignals> // If Display Power is not in normal operation we want to be in performance mode. When coming // back to normal mode, a grace period is given with DisplayPowerTimer. if (mDisplayPowerTimer && - (!mPolicy.isDisplayPowerStateNormal || mPolicy.displayPowerTimer == TimerState::Reset)) { + (mPolicy.displayPowerMode != hal::PowerMode::ON || + mPolicy.displayPowerTimer == TimerState::Reset)) { constexpr GlobalSignals kNoSignals; return {configs->getMaxRefreshRateByPolicy(), kNoSignals}; } diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index 0c72124119..a8043bf94c 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -186,7 +186,7 @@ public: // Indicates that touch interaction is taking place. void onTouchHint(); - void setDisplayPowerState(bool normal); + void setDisplayPowerMode(hal::PowerMode powerMode); VSyncDispatch& getVsyncDispatch() { return mVsyncSchedule->getDispatch(); } @@ -325,7 +325,7 @@ private: TimerState idleTimer = TimerState::Reset; TouchState touch = TouchState::Inactive; TimerState displayPowerTimer = TimerState::Expired; - bool isDisplayPowerStateNormal = true; + hal::PowerMode displayPowerMode = hal::PowerMode::ON; // Chosen display mode. DisplayModePtr mode; diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp index bdcab515f2..13cd304a5f 100644 --- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp @@ -146,6 +146,11 @@ bool VSyncReactor::periodConfirmed(nsecs_t vsync_timestamp, std::optional<nsecs_ return false; } + if (mDisplayPowerMode == hal::PowerMode::DOZE || + mDisplayPowerMode == hal::PowerMode::DOZE_SUSPEND) { + return true; + } + if (!mLastHwVsync && !HwcVsyncPeriod) { return false; } @@ -206,6 +211,11 @@ bool VSyncReactor::addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> return mMoreSamplesNeeded; } +void VSyncReactor::setDisplayPowerMode(hal::PowerMode powerMode) { + std::scoped_lock lock(mMutex); + mDisplayPowerMode = powerMode; +} + void VSyncReactor::dump(std::string& result) const { std::lock_guard lock(mMutex); StringAppendF(&result, "VsyncReactor in use\n"); diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h index 6a1950ac77..4501487392 100644 --- a/services/surfaceflinger/Scheduler/VSyncReactor.h +++ b/services/surfaceflinger/Scheduler/VSyncReactor.h @@ -49,6 +49,8 @@ public: bool addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod, bool* periodFlushed) final; + void setDisplayPowerMode(hal::PowerMode powerMode) final; + void dump(std::string& result) const final; private: @@ -73,6 +75,8 @@ private: std::optional<nsecs_t> mPeriodTransitioningTo GUARDED_BY(mMutex); std::optional<nsecs_t> mLastHwVsync GUARDED_BY(mMutex); + hal::PowerMode mDisplayPowerMode GUARDED_BY(mMutex) = hal::PowerMode::ON; + const bool mSupportKernelIdleTimer = false; }; diff --git a/services/surfaceflinger/Scheduler/VsyncController.h b/services/surfaceflinger/Scheduler/VsyncController.h index 59f65372a9..726a420649 100644 --- a/services/surfaceflinger/Scheduler/VsyncController.h +++ b/services/surfaceflinger/Scheduler/VsyncController.h @@ -18,7 +18,10 @@ #include <cstddef> #include <memory> +#include <mutex> +#include <DisplayHardware/HWComposer.h> +#include <DisplayHardware/Hal.h> #include <ui/FenceTime.h> #include <utils/Mutex.h> #include <utils/RefBase.h> @@ -70,6 +73,13 @@ public: */ virtual void setIgnorePresentFences(bool ignore) = 0; + /* + * Sets the primary display power mode to the controller. + * + * \param [in] powerMode + */ + virtual void setDisplayPowerMode(hal::PowerMode powerMode) = 0; + virtual void dump(std::string& result) const = 0; protected: diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 6a17cd8881..6e74eef068 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -444,6 +444,11 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI } mIgnoreHdrCameraLayers = ignore_hdr_camera_layers(false); + + // Power hint session mode, representing which hint(s) to send: early, late, or both) + mPowerHintSessionMode = + {.late = base::GetBoolProperty("debug.sf.send_late_power_session_hint"s, true), + .early = base::GetBoolProperty("debug.sf.send_early_power_session_hint"s, true)}; } LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() { @@ -1991,12 +1996,6 @@ nsecs_t SurfaceFlinger::calculateExpectedPresentTime(DisplayStatInfo stats) cons bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expectedVsyncTime) FTL_FAKE_GUARD(kMainThreadContext) { - // we set this once at the beginning of commit to ensure consistency throughout the whole frame - mPowerHintSessionData.sessionEnabled = mPowerAdvisor->usePowerHintSession(); - if (mPowerHintSessionData.sessionEnabled) { - mPowerHintSessionData.commitStart = systemTime(); - } - // calculate the expected present time once and use the cached // value throughout this frame to make sure all layers are // seeing this same value. @@ -2010,10 +2009,6 @@ bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expected const nsecs_t lastScheduledPresentTime = mScheduledPresentTime; mScheduledPresentTime = expectedVsyncTime; - if (mPowerHintSessionData.sessionEnabled) { - mPowerAdvisor->setTargetWorkDuration(mExpectedPresentTime - - mPowerHintSessionData.commitStart); - } const auto vsyncIn = [&] { if (!ATRACE_ENABLED()) return 0.f; return (mExpectedPresentTime - systemTime()) / 1e6f; @@ -2089,6 +2084,30 @@ bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expected } } + // Save this once per commit + composite to ensure consistency + mPowerHintSessionEnabled = mPowerAdvisor->usePowerHintSession(); + if (mPowerHintSessionEnabled) { + nsecs_t vsyncPeriod; + { + Mutex::Autolock lock(mStateLock); + vsyncPeriod = getVsyncPeriodFromHWC(); + } + mPowerAdvisor->setCommitStart(frameTime); + mPowerAdvisor->setExpectedPresentTime(mExpectedPresentTime); + const nsecs_t idealSfWorkDuration = + mVsyncModulator->getVsyncConfig().sfWorkDuration.count(); + // Frame delay is how long we should have minus how long we actually have + mPowerAdvisor->setFrameDelay(idealSfWorkDuration - (mExpectedPresentTime - frameTime)); + mPowerAdvisor->setTotalFrameTargetWorkDuration(idealSfWorkDuration); + mPowerAdvisor->setTargetWorkDuration(vsyncPeriod); + + // Send early hint here to make sure there's not another frame pending + if (mPowerHintSessionMode.early) { + // Send a rough prediction for this frame based on last frame's timing info + mPowerAdvisor->sendPredictedWorkDuration(); + } + } + if (mTracingEnabledChanged) { mLayerTracingEnabled = mLayerTracing.isEnabled(); mTracingEnabledChanged = false; @@ -2165,16 +2184,15 @@ void SurfaceFlinger::composite(nsecs_t frameTime, int64_t vsyncId) FTL_FAKE_GUARD(kMainThreadContext) { ATRACE_FORMAT("%s %" PRId64, __func__, vsyncId); - if (mPowerHintSessionData.sessionEnabled) { - mPowerHintSessionData.compositeStart = systemTime(); - } - compositionengine::CompositionRefreshArgs refreshArgs; const auto& displays = FTL_FAKE_GUARD(mStateLock, mDisplays); refreshArgs.outputs.reserve(displays.size()); + std::vector<DisplayId> displayIds; for (const auto& [_, display] : displays) { refreshArgs.outputs.push_back(display->getCompositionDisplay()); + displayIds.push_back(display->getId()); } + mPowerAdvisor->setDisplays(displayIds); mDrawingState.traverseInZOrder([&refreshArgs](Layer* layer) { if (auto layerFE = layer->getCompositionEngineLayerFE()) refreshArgs.layers.push_back(layerFE); @@ -2222,12 +2240,16 @@ void SurfaceFlinger::composite(nsecs_t frameTime, int64_t vsyncId) mCompositionEngine->present(refreshArgs); - if (mPowerHintSessionData.sessionEnabled) { - mPowerHintSessionData.presentEnd = systemTime(); - } - mTimeStats->recordFrameDuration(frameTime, systemTime()); + // Send a power hint hint after presentation is finished + if (mPowerHintSessionEnabled) { + mPowerAdvisor->setPresentFenceTime(mPreviousPresentFences[0].fenceTime->getSignalTime()); + if (mPowerHintSessionMode.late) { + mPowerAdvisor->sendActualWorkDuration(); + } + } + if (mScheduler->onPostComposition(presentTime)) { scheduleComposite(FrameHint::kNone); } @@ -2272,11 +2294,8 @@ void SurfaceFlinger::composite(nsecs_t frameTime, int64_t vsyncId) scheduleCommit(FrameHint::kNone); } - // calculate total render time for performance hinting if adpf cpu hint is enabled, - if (mPowerHintSessionData.sessionEnabled) { - const nsecs_t flingerDuration = - (mPowerHintSessionData.presentEnd - mPowerHintSessionData.commitStart); - mPowerAdvisor->sendActualWorkDuration(flingerDuration, mPowerHintSessionData.presentEnd); + if (mPowerHintSessionEnabled) { + mPowerAdvisor->setCompositeEnd(systemTime()); } } @@ -4929,7 +4948,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: if (isDisplayActiveLocked(display)) { mTimeStats->setPowerMode(mode); mRefreshRateStats->setPowerMode(mode); - mScheduler->setDisplayPowerState(mode == hal::PowerMode::ON); + mScheduler->setDisplayPowerMode(mode); } ALOGD("Finished setting power mode %d on display %s", mode, to_string(displayId).c_str()); @@ -6895,9 +6914,7 @@ status_t SurfaceFlinger::setDesiredDisplayModeSpecsInternal( return NO_ERROR; } - status_t setPolicyResult = overridePolicy - ? display->refreshRateConfigs().setOverridePolicy(policy) - : display->refreshRateConfigs().setDisplayManagerPolicy(*policy); + const status_t setPolicyResult = display->setRefreshRatePolicy(policy, overridePolicy); if (setPolicyResult < 0) { return BAD_VALUE; } diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index f14c755c2b..83134a2ebc 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -1435,12 +1435,12 @@ private: return mScheduler->getLayerFramerate(now, id); } + bool mPowerHintSessionEnabled; + struct { - bool sessionEnabled = false; - nsecs_t commitStart; - nsecs_t compositeStart; - nsecs_t presentEnd; - } mPowerHintSessionData GUARDED_BY(kMainThreadContext); + bool late = false; + bool early = false; + } mPowerHintSessionMode; nsecs_t mAnimationTransactionTimeout = s2ns(5); diff --git a/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp b/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp index 9ab35d741a..53de4a6e56 100644 --- a/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp +++ b/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp @@ -52,6 +52,8 @@ protected: void verifyAndClearExpectations(); void sendActualWorkDurationGroup(std::vector<WorkDuration> durations, std::chrono::nanoseconds sleepBeforeLastSend); + std::chrono::nanoseconds mAllowedDeviation; + std::chrono::nanoseconds mStaleTimeout; }; void AidlPowerHalWrapperTest::SetUp() { @@ -59,6 +61,9 @@ void AidlPowerHalWrapperTest::SetUp() { mMockSession = new NiceMock<MockIPowerHintSession>(); ON_CALL(*mMockHal.get(), getHintSessionPreferredRate(_)).WillByDefault(Return(Status::ok())); mWrapper = std::make_unique<AidlPowerHalWrapper>(mMockHal); + mWrapper->setAllowedActualDeviation(std::chrono::nanoseconds{10ms}.count()); + mAllowedDeviation = std::chrono::nanoseconds{mWrapper->mAllowedActualDeviation}; + mStaleTimeout = AidlPowerHalWrapper::kStaleTimeout; } void AidlPowerHalWrapperTest::verifyAndClearExpectations() { @@ -76,6 +81,7 @@ void AidlPowerHalWrapperTest::sendActualWorkDurationGroup( mWrapper->sendActualWorkDuration(duration.durationNanos, duration.timeStampNanos); } } + WorkDuration toWorkDuration(std::chrono::nanoseconds durationNanos, int64_t timeStampNanos) { WorkDuration duration; duration.durationNanos = durationNanos.count(); @@ -83,6 +89,10 @@ WorkDuration toWorkDuration(std::chrono::nanoseconds durationNanos, int64_t time return duration; } +WorkDuration toWorkDuration(std::pair<std::chrono::nanoseconds, nsecs_t> timePair) { + return toWorkDuration(timePair.first, timePair.second); +} + std::string printWorkDurations(const ::std::vector<WorkDuration>& durations) { std::ostringstream os; for (auto duration : durations) { @@ -112,7 +122,7 @@ TEST_F(AidlPowerHalWrapperTest, startPowerHintSession) { EXPECT_FALSE(mWrapper->startPowerHintSession()); } -TEST_F(AidlPowerHalWrapperTest, restartNewPoserHintSessionWithNewThreadIds) { +TEST_F(AidlPowerHalWrapperTest, restartNewPowerHintSessionWithNewThreadIds) { ASSERT_TRUE(mWrapper->supportsPowerHintSession()); std::vector<int32_t> threadIds = {1, 2}; @@ -149,12 +159,8 @@ TEST_F(AidlPowerHalWrapperTest, setTargetWorkDuration) { std::chrono::nanoseconds base = 100ms; // test cases with target work duration and whether it should update hint against baseline 100ms - const std::vector<std::pair<std::chrono::nanoseconds, bool>> testCases = {{0ms, false}, - {-1ms, false}, - {200ms, true}, - {2ms, true}, - {91ms, false}, - {109ms, false}}; + const std::vector<std::pair<std::chrono::nanoseconds, bool>> testCases = + {{0ms, true}, {-1ms, true}, {200ms, true}, {2ms, true}, {100ms, false}, {109ms, true}}; for (const auto& test : testCases) { // reset to 100ms baseline @@ -200,21 +206,21 @@ TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration) { // 100ms const std::vector<std::pair<std::vector<std::pair<std::chrono::nanoseconds, nsecs_t>>, bool>> testCases = {{{{-1ms, 100}}, false}, - {{{91ms, 100}}, false}, - {{{109ms, 100}}, false}, + {{{100ms - (mAllowedDeviation / 2), 100}}, false}, + {{{100ms + (mAllowedDeviation / 2), 100}}, false}, + {{{100ms + (mAllowedDeviation + 1ms), 100}}, true}, + {{{100ms - (mAllowedDeviation + 1ms), 100}}, true}, {{{100ms, 100}, {200ms, 200}}, true}, {{{100ms, 500}, {100ms, 600}, {3ms, 600}}, true}}; for (const auto& test : testCases) { // reset actual duration - sendActualWorkDurationGroup({base}, 80ms); + sendActualWorkDurationGroup({base}, mStaleTimeout); auto raw = test.first; std::vector<WorkDuration> durations(raw.size()); std::transform(raw.begin(), raw.end(), durations.begin(), - [](std::pair<std::chrono::nanoseconds, nsecs_t> d) { - return toWorkDuration(d.first, d.second); - }); + [](auto d) { return toWorkDuration(d); }); EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(durations)) .Times(test.second ? 1 : 0); sendActualWorkDurationGroup(durations, 0ms); @@ -222,40 +228,6 @@ TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration) { } } -TEST_F(AidlPowerHalWrapperTest, sendAdjustedActualWorkDuration) { - ASSERT_TRUE(mWrapper->supportsPowerHintSession()); - - std::vector<int32_t> threadIds = {1, 2}; - mWrapper->setPowerHintSessionThreadIds(threadIds); - EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _)) - .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok()))); - ASSERT_TRUE(mWrapper->startPowerHintSession()); - verifyAndClearExpectations(); - - std::chrono::nanoseconds lastTarget = 100ms; - EXPECT_CALL(*mMockSession.get(), updateTargetWorkDuration(lastTarget.count())).Times(1); - mWrapper->setTargetWorkDuration(lastTarget.count()); - std::chrono::nanoseconds newTarget = 105ms; - mWrapper->setTargetWorkDuration(newTarget.count()); - EXPECT_CALL(*mMockSession.get(), updateTargetWorkDuration(newTarget.count())).Times(0); - std::chrono::nanoseconds actual = 21ms; - // 100 / 105 * 21ms = 20ms - std::chrono::nanoseconds expectedActualSent = 20ms; - std::vector<WorkDuration> expectedDurations = {toWorkDuration(expectedActualSent, 1)}; - - EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(_)) - .WillOnce(DoAll( - [expectedDurations](const ::std::vector<WorkDuration>& durationsSent) { - EXPECT_EQ(expectedDurations, durationsSent) - << base::StringPrintf("actual sent: %s vs expected: %s", - printWorkDurations(durationsSent).c_str(), - printWorkDurations(expectedDurations) - .c_str()); - }, - Return(Status::ok()))); - mWrapper->sendActualWorkDuration(actual.count(), 1); -} - TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration_exceedsStaleTime) { ASSERT_TRUE(mWrapper->supportsPowerHintSession()); @@ -269,22 +241,23 @@ TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration_exceedsStaleTime) { auto base = toWorkDuration(100ms, 0); // test cases with actual work durations and whether it should update hint against baseline // 100ms - const std::vector<std::pair<std::vector<std::pair<std::chrono::nanoseconds, nsecs_t>>, bool>> - testCases = {{{{91ms, 100}}, true}, {{{109ms, 100}}, true}}; + const std::vector<std::tuple<std::vector<std::pair<std::chrono::nanoseconds, nsecs_t>>, + std::chrono::nanoseconds, bool>> + testCases = {{{{100ms, 100}}, mStaleTimeout, true}, + {{{100ms + (mAllowedDeviation / 2), 100}}, mStaleTimeout, true}, + {{{100ms, 100}}, mStaleTimeout / 2, false}}; for (const auto& test : testCases) { // reset actual duration - sendActualWorkDurationGroup({base}, 80ms); + sendActualWorkDurationGroup({base}, mStaleTimeout); - auto raw = test.first; + auto raw = std::get<0>(test); std::vector<WorkDuration> durations(raw.size()); std::transform(raw.begin(), raw.end(), durations.begin(), - [](std::pair<std::chrono::nanoseconds, nsecs_t> d) { - return toWorkDuration(d.first, d.second); - }); + [](auto d) { return toWorkDuration(d); }); EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(durations)) - .Times(test.second ? 1 : 0); - sendActualWorkDurationGroup(durations, 80ms); + .Times(std::get<2>(test) ? 1 : 0); + sendActualWorkDurationGroup(durations, std::get<1>(test)); verifyAndClearExpectations(); } } diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp index aab27957c3..93c809e2fd 100644 --- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp +++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp @@ -159,8 +159,8 @@ TEST_F(SchedulerTest, chooseRefreshRateForContentIsNoopWhenModeSwitchingIsNotSup mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer); ASSERT_EQ(0u, mScheduler->getNumActiveLayers()); - constexpr bool kPowerStateNormal = true; - mScheduler->setDisplayPowerState(kPowerStateNormal); + constexpr hal::PowerMode kPowerModeOn = hal::PowerMode::ON; + mScheduler->setDisplayPowerMode(kPowerModeOn); constexpr uint32_t kDisplayArea = 999'999; mScheduler->onActiveDisplayAreaChanged(kDisplayArea); @@ -226,8 +226,8 @@ TEST_F(SchedulerTest, chooseRefreshRateForContentSelectsMaxRefreshRate) { mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer); - constexpr bool kPowerStateNormal = true; - mScheduler->setDisplayPowerState(kPowerStateNormal); + constexpr hal::PowerMode kPowerModeOn = hal::PowerMode::ON; + mScheduler->setDisplayPowerMode(kPowerModeOn); constexpr uint32_t kDisplayArea = 999'999; mScheduler->onActiveDisplayAreaChanged(kDisplayArea); diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp index 8de9e4be29..c2d87f2484 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp @@ -74,6 +74,7 @@ void SurfaceFlingerPowerHintTest::SetUp() { mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine)); mFlinger.setupTimeStats(std::shared_ptr<TimeStats>(mTimeStats)); mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer)); + mFlinger.setPowerHintSessionMode(true, true); mFlinger.setupPowerAdvisor(std::unique_ptr<Hwc2::PowerAdvisor>(mPowerAdvisor)); static constexpr bool kIsPrimary = true; FakeHwcDisplayInjector(DEFAULT_DISPLAY_ID, hal::DisplayType::PHYSICAL, kIsPrimary) @@ -142,10 +143,7 @@ TEST_F(SurfaceFlingerPowerHintTest, sendDurationsIncludingHwcWaitTime) { std::this_thread::sleep_for(mockHwcRunTime); return hardware::graphics::composer::V2_1::Error::NONE; }); - EXPECT_CALL(*mPowerAdvisor, - sendActualWorkDuration(Gt(mockHwcRunTime.count()), - Gt(now + mockHwcRunTime.count()))) - .Times(1); + EXPECT_CALL(*mPowerAdvisor, sendActualWorkDuration()).Times(1); static constexpr bool kVsyncId = 123; // arbitrary mFlinger.commitAndComposite(now, kVsyncId, now + mockVsyncPeriod.count()); } diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index f1a69fb46e..b70fdcd93e 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -330,6 +330,10 @@ public: layer->mDrawingParent = drawingParent; } + void setPowerHintSessionMode(bool early, bool late) { + mFlinger->mPowerHintSessionMode = {.late = late, .early = early}; + } + /* ------------------------------------------------------------------------ * Forwarding for functions being tested */ diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp index 4eb90558ab..30a3f9a4a7 100644 --- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp @@ -349,6 +349,23 @@ TEST_F(VSyncReactorTest, addResyncSamplePeriodChanges) { } } +TEST_F(VSyncReactorTest, addHwVsyncTimestampDozePreempt) { + bool periodFlushed = false; + nsecs_t const newPeriod = 4000; + + mReactor.startPeriodTransition(newPeriod); + + auto time = 0; + // If the power mode is not DOZE or DOZE_SUSPEND, it is still collecting timestamps. + EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed)); + EXPECT_FALSE(periodFlushed); + + // Set power mode to DOZE to trigger period flushing. + mReactor.setDisplayPowerMode(hal::PowerMode::DOZE); + EXPECT_FALSE(mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed)); + EXPECT_TRUE(periodFlushed); +} + TEST_F(VSyncReactorTest, addPresentFenceWhileAwaitingPeriodConfirmationRequestsHwVsync) { auto time = 0; bool periodFlushed = false; diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h index c598cbc28e..d6dca45188 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h @@ -36,11 +36,32 @@ public: MOCK_METHOD(bool, usePowerHintSession, (), (override)); MOCK_METHOD(bool, supportsPowerHintSession, (), (override)); MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override)); - MOCK_METHOD(void, setTargetWorkDuration, (int64_t targetDurationNanos), (override)); - MOCK_METHOD(void, sendActualWorkDuration, (int64_t actualDurationNanos, nsecs_t timestamp), - (override)); + MOCK_METHOD(void, setTargetWorkDuration, (int64_t targetDuration), (override)); + MOCK_METHOD(void, sendActualWorkDuration, (), (override)); + MOCK_METHOD(void, sendPredictedWorkDuration, (), (override)); MOCK_METHOD(void, enablePowerHint, (bool enabled), (override)); MOCK_METHOD(bool, startPowerHintSession, (const std::vector<int32_t>& threadIds), (override)); + MOCK_METHOD(void, setGpuFenceTime, + (DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime), (override)); + MOCK_METHOD(void, setHwcValidateTiming, + (DisplayId displayId, nsecs_t valiateStartTime, nsecs_t validateEndTime), + (override)); + MOCK_METHOD(void, setHwcPresentTiming, + (DisplayId displayId, nsecs_t presentStartTime, nsecs_t presentEndTime), + (override)); + MOCK_METHOD(void, setSkippedValidate, (DisplayId displayId, bool skipped), (override)); + MOCK_METHOD(void, setRequiresClientComposition, + (DisplayId displayId, bool requiresClientComposition), (override)); + MOCK_METHOD(void, setExpectedPresentTime, (nsecs_t expectedPresentTime), (override)); + MOCK_METHOD(void, setPresentFenceTime, (nsecs_t presentFenceTime), (override)); + MOCK_METHOD(void, setHwcPresentDelayedTime, + (DisplayId displayId, + std::chrono::steady_clock::time_point earliestFrameStartTime)); + MOCK_METHOD(void, setFrameDelay, (nsecs_t frameDelayDuration), (override)); + MOCK_METHOD(void, setCommitStart, (nsecs_t commitStartTime), (override)); + MOCK_METHOD(void, setCompositeEnd, (nsecs_t compositeEndtime), (override)); + MOCK_METHOD(void, setDisplays, (std::vector<DisplayId> & displayIds), (override)); + MOCK_METHOD(void, setTotalFrameTargetWorkDuration, (int64_t targetDuration), (override)); }; } // namespace android::Hwc2::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h index 314f681545..4ef91dacb2 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h +++ b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h @@ -31,6 +31,7 @@ public: MOCK_METHOD3(addHwVsyncTimestamp, bool(nsecs_t, std::optional<nsecs_t>, bool*)); MOCK_METHOD1(startPeriodTransition, void(nsecs_t)); MOCK_METHOD1(setIgnorePresentFences, void(bool)); + MOCK_METHOD(void, setDisplayPowerMode, (hal::PowerMode), (override)); MOCK_CONST_METHOD1(dump, void(std::string&)); }; |