Loading libs/gui/Choreographer.cpp +32 −14 Original line number Diff line number Diff line Loading @@ -143,9 +143,9 @@ Choreographer::~Choreographer() { void Choreographer::postFrameCallbackDelayed(AChoreographer_frameCallback cb, AChoreographer_frameCallback64 cb64, AChoreographer_vsyncCallback vsyncCallback, void* data, nsecs_t delay) { nsecs_t delay, CallbackType callbackType) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); FrameCallback callback{cb, cb64, vsyncCallback, data, now + delay}; FrameCallback callback{cb, cb64, vsyncCallback, data, now + delay, callbackType}; { std::lock_guard<std::mutex> _l{mLock}; mFrameCallbacks.push(callback); Loading Loading @@ -285,18 +285,8 @@ void Choreographer::handleRefreshRateUpdates() { } } void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t, VsyncEventData vsyncEventData) { std::vector<FrameCallback> callbacks{}; { std::lock_guard<std::mutex> _l{mLock}; nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); while (!mFrameCallbacks.empty() && mFrameCallbacks.top().dueTime < now) { callbacks.push_back(mFrameCallbacks.top()); mFrameCallbacks.pop(); } } mLastVsyncEventData = vsyncEventData; void Choreographer::dispatchCallbacks(const std::vector<FrameCallback>& callbacks, VsyncEventData vsyncEventData, nsecs_t timestamp) { for (const auto& cb : callbacks) { if (cb.vsyncCallback != nullptr) { ATRACE_FORMAT("AChoreographer_vsyncCallback %" PRId64, Loading @@ -319,6 +309,34 @@ void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t } } void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t, VsyncEventData vsyncEventData) { std::vector<FrameCallback> animationCallbacks{}; std::vector<FrameCallback> inputCallbacks{}; { std::lock_guard<std::mutex> _l{mLock}; nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); while (!mFrameCallbacks.empty() && mFrameCallbacks.top().dueTime < now) { if (mFrameCallbacks.top().callbackType == CALLBACK_INPUT) { inputCallbacks.push_back(mFrameCallbacks.top()); } else { animationCallbacks.push_back(mFrameCallbacks.top()); } mFrameCallbacks.pop(); } } mLastVsyncEventData = vsyncEventData; // Callbacks with type CALLBACK_INPUT should always run first { ATRACE_FORMAT("CALLBACK_INPUT"); dispatchCallbacks(inputCallbacks, vsyncEventData, timestamp); } { ATRACE_FORMAT("CALLBACK_ANIMATION"); dispatchCallbacks(animationCallbacks, vsyncEventData, timestamp); } } void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool connected) { ALOGV("choreographer %p ~ received hotplug event (displayId=%s, connected=%s), ignoring.", this, to_string(displayId).c_str(), toString(connected)); Loading libs/gui/include/gui/Choreographer.h +9 −1 Original line number Diff line number Diff line Loading @@ -28,12 +28,18 @@ namespace android { using gui::VsyncEventData; enum CallbackType : int8_t { CALLBACK_INPUT, CALLBACK_ANIMATION, }; struct FrameCallback { AChoreographer_frameCallback callback; AChoreographer_frameCallback64 callback64; AChoreographer_vsyncCallback vsyncCallback; void* data; nsecs_t dueTime; CallbackType callbackType; inline bool operator<(const FrameCallback& rhs) const { // Note that this is intentionally flipped because we want callbacks due sooner to be at Loading Loading @@ -78,7 +84,7 @@ public: void postFrameCallbackDelayed(AChoreographer_frameCallback cb, AChoreographer_frameCallback64 cb64, AChoreographer_vsyncCallback vsyncCallback, void* data, nsecs_t delay); nsecs_t delay, CallbackType callbackType); void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data) EXCLUDES(gChoreographers.lock); void unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data); Loading Loading @@ -109,6 +115,8 @@ private: void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count, VsyncEventData vsyncEventData) override; void dispatchCallbacks(const std::vector<FrameCallback>&, VsyncEventData vsyncEventData, nsecs_t timestamp); void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override; void dispatchHotplugConnectionError(nsecs_t timestamp, int32_t connectionError) override; void dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t modeId, Loading libs/gui/tests/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ cc_test { "BLASTBufferQueue_test.cpp", "BufferItemConsumer_test.cpp", "BufferQueue_test.cpp", "Choreographer_test.cpp", "CompositorTiming_test.cpp", "CpuConsumer_test.cpp", "EndToEndNativeInputTest.cpp", Loading Loading @@ -61,6 +62,7 @@ cc_test { "libSurfaceFlingerProp", "libGLESv1_CM", "libinput", "libnativedisplay", ], static_libs: [ Loading libs/gui/tests/Choreographer_test.cpp 0 → 100644 +88 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "Choreographer_test" #include <android-base/stringprintf.h> #include <android/choreographer.h> #include <gtest/gtest.h> #include <gui/Choreographer.h> #include <utils/Looper.h> #include <chrono> #include <future> #include <string> namespace android { class ChoreographerTest : public ::testing::Test {}; struct VsyncCallback { std::atomic<bool> completePromise{false}; std::chrono::nanoseconds frameTime{0LL}; std::chrono::nanoseconds receivedCallbackTime{0LL}; void onVsyncCallback(const AChoreographerFrameCallbackData* callbackData) { frameTime = std::chrono::nanoseconds{ AChoreographerFrameCallbackData_getFrameTimeNanos(callbackData)}; receivedCallbackTime = std::chrono::nanoseconds{systemTime(SYSTEM_TIME_MONOTONIC)}; completePromise.store(true); } bool callbackReceived() { return completePromise.load(); } }; static void vsyncCallback(const AChoreographerFrameCallbackData* callbackData, void* data) { VsyncCallback* cb = static_cast<VsyncCallback*>(data); cb->onVsyncCallback(callbackData); } TEST_F(ChoreographerTest, InputCallbackBeforeAnimation) { sp<Looper> looper = Looper::prepare(0); Choreographer* choreographer = Choreographer::getForThread(); VsyncCallback animationCb; VsyncCallback inputCb; choreographer->postFrameCallbackDelayed(nullptr, nullptr, vsyncCallback, &animationCb, 0, CALLBACK_ANIMATION); choreographer->postFrameCallbackDelayed(nullptr, nullptr, vsyncCallback, &inputCb, 0, CALLBACK_INPUT); nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); nsecs_t currTime; int pollResult; do { pollResult = looper->pollOnce(16); currTime = systemTime(SYSTEM_TIME_MONOTONIC); } while (!(inputCb.callbackReceived() && animationCb.callbackReceived()) && (pollResult != Looper::POLL_TIMEOUT && pollResult != Looper::POLL_ERROR) && (currTime - startTime < 3000)); ASSERT_TRUE(inputCb.callbackReceived()) << "did not receive input callback"; ASSERT_TRUE(animationCb.callbackReceived()) << "did not receive animation callback"; ASSERT_EQ(inputCb.frameTime, animationCb.frameTime) << android::base::StringPrintf("input and animation callback frame times don't match. " "inputFrameTime=%lld animationFrameTime=%lld", inputCb.frameTime.count(), animationCb.frameTime.count()); ASSERT_LT(inputCb.receivedCallbackTime, animationCb.receivedCallbackTime) << android::base::StringPrintf("input callback was not called first. " "inputCallbackTime=%lld animationCallbackTime=%lld", inputCb.frameTime.count(), animationCb.frameTime.count()); } } // namespace android No newline at end of file libs/nativedisplay/AChoreographer.cpp +7 −5 Original line number Diff line number Diff line Loading @@ -148,29 +148,31 @@ AChoreographer* AChoreographer_getInstance() { void AChoreographer_postFrameCallback(AChoreographer* choreographer, AChoreographer_frameCallback callback, void* data) { AChoreographer_to_Choreographer(choreographer) ->postFrameCallbackDelayed(callback, nullptr, nullptr, data, 0); ->postFrameCallbackDelayed(callback, nullptr, nullptr, data, 0, CALLBACK_ANIMATION); } void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer, AChoreographer_frameCallback callback, void* data, long delayMillis) { AChoreographer_to_Choreographer(choreographer) ->postFrameCallbackDelayed(callback, nullptr, nullptr, data, ms2ns(delayMillis)); ->postFrameCallbackDelayed(callback, nullptr, nullptr, data, ms2ns(delayMillis), CALLBACK_ANIMATION); } void AChoreographer_postVsyncCallback(AChoreographer* choreographer, AChoreographer_vsyncCallback callback, void* data) { AChoreographer_to_Choreographer(choreographer) ->postFrameCallbackDelayed(nullptr, nullptr, callback, data, 0); ->postFrameCallbackDelayed(nullptr, nullptr, callback, data, 0, CALLBACK_ANIMATION); } void AChoreographer_postFrameCallback64(AChoreographer* choreographer, AChoreographer_frameCallback64 callback, void* data) { AChoreographer_to_Choreographer(choreographer) ->postFrameCallbackDelayed(nullptr, callback, nullptr, data, 0); ->postFrameCallbackDelayed(nullptr, callback, nullptr, data, 0, CALLBACK_ANIMATION); } void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer, AChoreographer_frameCallback64 callback, void* data, uint32_t delayMillis) { AChoreographer_to_Choreographer(choreographer) ->postFrameCallbackDelayed(nullptr, callback, nullptr, data, ms2ns(delayMillis)); ->postFrameCallbackDelayed(nullptr, callback, nullptr, data, ms2ns(delayMillis), CALLBACK_ANIMATION); } void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer, AChoreographer_refreshRateCallback callback, Loading Loading
libs/gui/Choreographer.cpp +32 −14 Original line number Diff line number Diff line Loading @@ -143,9 +143,9 @@ Choreographer::~Choreographer() { void Choreographer::postFrameCallbackDelayed(AChoreographer_frameCallback cb, AChoreographer_frameCallback64 cb64, AChoreographer_vsyncCallback vsyncCallback, void* data, nsecs_t delay) { nsecs_t delay, CallbackType callbackType) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); FrameCallback callback{cb, cb64, vsyncCallback, data, now + delay}; FrameCallback callback{cb, cb64, vsyncCallback, data, now + delay, callbackType}; { std::lock_guard<std::mutex> _l{mLock}; mFrameCallbacks.push(callback); Loading Loading @@ -285,18 +285,8 @@ void Choreographer::handleRefreshRateUpdates() { } } void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t, VsyncEventData vsyncEventData) { std::vector<FrameCallback> callbacks{}; { std::lock_guard<std::mutex> _l{mLock}; nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); while (!mFrameCallbacks.empty() && mFrameCallbacks.top().dueTime < now) { callbacks.push_back(mFrameCallbacks.top()); mFrameCallbacks.pop(); } } mLastVsyncEventData = vsyncEventData; void Choreographer::dispatchCallbacks(const std::vector<FrameCallback>& callbacks, VsyncEventData vsyncEventData, nsecs_t timestamp) { for (const auto& cb : callbacks) { if (cb.vsyncCallback != nullptr) { ATRACE_FORMAT("AChoreographer_vsyncCallback %" PRId64, Loading @@ -319,6 +309,34 @@ void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t } } void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t, VsyncEventData vsyncEventData) { std::vector<FrameCallback> animationCallbacks{}; std::vector<FrameCallback> inputCallbacks{}; { std::lock_guard<std::mutex> _l{mLock}; nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); while (!mFrameCallbacks.empty() && mFrameCallbacks.top().dueTime < now) { if (mFrameCallbacks.top().callbackType == CALLBACK_INPUT) { inputCallbacks.push_back(mFrameCallbacks.top()); } else { animationCallbacks.push_back(mFrameCallbacks.top()); } mFrameCallbacks.pop(); } } mLastVsyncEventData = vsyncEventData; // Callbacks with type CALLBACK_INPUT should always run first { ATRACE_FORMAT("CALLBACK_INPUT"); dispatchCallbacks(inputCallbacks, vsyncEventData, timestamp); } { ATRACE_FORMAT("CALLBACK_ANIMATION"); dispatchCallbacks(animationCallbacks, vsyncEventData, timestamp); } } void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool connected) { ALOGV("choreographer %p ~ received hotplug event (displayId=%s, connected=%s), ignoring.", this, to_string(displayId).c_str(), toString(connected)); Loading
libs/gui/include/gui/Choreographer.h +9 −1 Original line number Diff line number Diff line Loading @@ -28,12 +28,18 @@ namespace android { using gui::VsyncEventData; enum CallbackType : int8_t { CALLBACK_INPUT, CALLBACK_ANIMATION, }; struct FrameCallback { AChoreographer_frameCallback callback; AChoreographer_frameCallback64 callback64; AChoreographer_vsyncCallback vsyncCallback; void* data; nsecs_t dueTime; CallbackType callbackType; inline bool operator<(const FrameCallback& rhs) const { // Note that this is intentionally flipped because we want callbacks due sooner to be at Loading Loading @@ -78,7 +84,7 @@ public: void postFrameCallbackDelayed(AChoreographer_frameCallback cb, AChoreographer_frameCallback64 cb64, AChoreographer_vsyncCallback vsyncCallback, void* data, nsecs_t delay); nsecs_t delay, CallbackType callbackType); void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data) EXCLUDES(gChoreographers.lock); void unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data); Loading Loading @@ -109,6 +115,8 @@ private: void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count, VsyncEventData vsyncEventData) override; void dispatchCallbacks(const std::vector<FrameCallback>&, VsyncEventData vsyncEventData, nsecs_t timestamp); void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override; void dispatchHotplugConnectionError(nsecs_t timestamp, int32_t connectionError) override; void dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t modeId, Loading
libs/gui/tests/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ cc_test { "BLASTBufferQueue_test.cpp", "BufferItemConsumer_test.cpp", "BufferQueue_test.cpp", "Choreographer_test.cpp", "CompositorTiming_test.cpp", "CpuConsumer_test.cpp", "EndToEndNativeInputTest.cpp", Loading Loading @@ -61,6 +62,7 @@ cc_test { "libSurfaceFlingerProp", "libGLESv1_CM", "libinput", "libnativedisplay", ], static_libs: [ Loading
libs/gui/tests/Choreographer_test.cpp 0 → 100644 +88 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "Choreographer_test" #include <android-base/stringprintf.h> #include <android/choreographer.h> #include <gtest/gtest.h> #include <gui/Choreographer.h> #include <utils/Looper.h> #include <chrono> #include <future> #include <string> namespace android { class ChoreographerTest : public ::testing::Test {}; struct VsyncCallback { std::atomic<bool> completePromise{false}; std::chrono::nanoseconds frameTime{0LL}; std::chrono::nanoseconds receivedCallbackTime{0LL}; void onVsyncCallback(const AChoreographerFrameCallbackData* callbackData) { frameTime = std::chrono::nanoseconds{ AChoreographerFrameCallbackData_getFrameTimeNanos(callbackData)}; receivedCallbackTime = std::chrono::nanoseconds{systemTime(SYSTEM_TIME_MONOTONIC)}; completePromise.store(true); } bool callbackReceived() { return completePromise.load(); } }; static void vsyncCallback(const AChoreographerFrameCallbackData* callbackData, void* data) { VsyncCallback* cb = static_cast<VsyncCallback*>(data); cb->onVsyncCallback(callbackData); } TEST_F(ChoreographerTest, InputCallbackBeforeAnimation) { sp<Looper> looper = Looper::prepare(0); Choreographer* choreographer = Choreographer::getForThread(); VsyncCallback animationCb; VsyncCallback inputCb; choreographer->postFrameCallbackDelayed(nullptr, nullptr, vsyncCallback, &animationCb, 0, CALLBACK_ANIMATION); choreographer->postFrameCallbackDelayed(nullptr, nullptr, vsyncCallback, &inputCb, 0, CALLBACK_INPUT); nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); nsecs_t currTime; int pollResult; do { pollResult = looper->pollOnce(16); currTime = systemTime(SYSTEM_TIME_MONOTONIC); } while (!(inputCb.callbackReceived() && animationCb.callbackReceived()) && (pollResult != Looper::POLL_TIMEOUT && pollResult != Looper::POLL_ERROR) && (currTime - startTime < 3000)); ASSERT_TRUE(inputCb.callbackReceived()) << "did not receive input callback"; ASSERT_TRUE(animationCb.callbackReceived()) << "did not receive animation callback"; ASSERT_EQ(inputCb.frameTime, animationCb.frameTime) << android::base::StringPrintf("input and animation callback frame times don't match. " "inputFrameTime=%lld animationFrameTime=%lld", inputCb.frameTime.count(), animationCb.frameTime.count()); ASSERT_LT(inputCb.receivedCallbackTime, animationCb.receivedCallbackTime) << android::base::StringPrintf("input callback was not called first. " "inputCallbackTime=%lld animationCallbackTime=%lld", inputCb.frameTime.count(), animationCb.frameTime.count()); } } // namespace android No newline at end of file
libs/nativedisplay/AChoreographer.cpp +7 −5 Original line number Diff line number Diff line Loading @@ -148,29 +148,31 @@ AChoreographer* AChoreographer_getInstance() { void AChoreographer_postFrameCallback(AChoreographer* choreographer, AChoreographer_frameCallback callback, void* data) { AChoreographer_to_Choreographer(choreographer) ->postFrameCallbackDelayed(callback, nullptr, nullptr, data, 0); ->postFrameCallbackDelayed(callback, nullptr, nullptr, data, 0, CALLBACK_ANIMATION); } void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer, AChoreographer_frameCallback callback, void* data, long delayMillis) { AChoreographer_to_Choreographer(choreographer) ->postFrameCallbackDelayed(callback, nullptr, nullptr, data, ms2ns(delayMillis)); ->postFrameCallbackDelayed(callback, nullptr, nullptr, data, ms2ns(delayMillis), CALLBACK_ANIMATION); } void AChoreographer_postVsyncCallback(AChoreographer* choreographer, AChoreographer_vsyncCallback callback, void* data) { AChoreographer_to_Choreographer(choreographer) ->postFrameCallbackDelayed(nullptr, nullptr, callback, data, 0); ->postFrameCallbackDelayed(nullptr, nullptr, callback, data, 0, CALLBACK_ANIMATION); } void AChoreographer_postFrameCallback64(AChoreographer* choreographer, AChoreographer_frameCallback64 callback, void* data) { AChoreographer_to_Choreographer(choreographer) ->postFrameCallbackDelayed(nullptr, callback, nullptr, data, 0); ->postFrameCallbackDelayed(nullptr, callback, nullptr, data, 0, CALLBACK_ANIMATION); } void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer, AChoreographer_frameCallback64 callback, void* data, uint32_t delayMillis) { AChoreographer_to_Choreographer(choreographer) ->postFrameCallbackDelayed(nullptr, callback, nullptr, data, ms2ns(delayMillis)); ->postFrameCallbackDelayed(nullptr, callback, nullptr, data, ms2ns(delayMillis), CALLBACK_ANIMATION); } void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer, AChoreographer_refreshRateCallback callback, Loading