Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 475c4131 authored by Chavi Weingarten's avatar Chavi Weingarten
Browse files

Add types for native Choreographer to ensure ordering

For now, added type CALLBACK_INPUT and CALLBACK_ANIMATION. The default
callback type will be CALLBACK_ANIMATION for the public APIs.
CALLBACK_INPUT will always run before CALLBACK_ANIMATION.

Test: ChoreographerTest
Bug: 324271765
Change-Id: I25c92b788bb83778d5b0608a47efe9756d42e32f
parent f8680e6d
Loading
Loading
Loading
Loading
+32 −14
Original line number Original line Diff line number Diff line
@@ -143,9 +143,9 @@ Choreographer::~Choreographer() {
void Choreographer::postFrameCallbackDelayed(AChoreographer_frameCallback cb,
void Choreographer::postFrameCallbackDelayed(AChoreographer_frameCallback cb,
                                             AChoreographer_frameCallback64 cb64,
                                             AChoreographer_frameCallback64 cb64,
                                             AChoreographer_vsyncCallback vsyncCallback, void* data,
                                             AChoreographer_vsyncCallback vsyncCallback, void* data,
                                             nsecs_t delay) {
                                             nsecs_t delay, CallbackType callbackType) {
    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
    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};
        std::lock_guard<std::mutex> _l{mLock};
        mFrameCallbacks.push(callback);
        mFrameCallbacks.push(callback);
@@ -285,18 +285,8 @@ void Choreographer::handleRefreshRateUpdates() {
    }
    }
}
}


void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t,
void Choreographer::dispatchCallbacks(const std::vector<FrameCallback>& callbacks,
                                  VsyncEventData vsyncEventData) {
                                      VsyncEventData vsyncEventData, nsecs_t timestamp) {
    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;
    for (const auto& cb : callbacks) {
    for (const auto& cb : callbacks) {
        if (cb.vsyncCallback != nullptr) {
        if (cb.vsyncCallback != nullptr) {
            ATRACE_FORMAT("AChoreographer_vsyncCallback %" PRId64,
            ATRACE_FORMAT("AChoreographer_vsyncCallback %" PRId64,
@@ -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) {
void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool connected) {
    ALOGV("choreographer %p ~ received hotplug event (displayId=%s, connected=%s), ignoring.", this,
    ALOGV("choreographer %p ~ received hotplug event (displayId=%s, connected=%s), ignoring.", this,
          to_string(displayId).c_str(), toString(connected));
          to_string(displayId).c_str(), toString(connected));
+9 −1
Original line number Original line Diff line number Diff line
@@ -28,12 +28,18 @@
namespace android {
namespace android {
using gui::VsyncEventData;
using gui::VsyncEventData;


enum CallbackType : int8_t {
    CALLBACK_INPUT,
    CALLBACK_ANIMATION,
};

struct FrameCallback {
struct FrameCallback {
    AChoreographer_frameCallback callback;
    AChoreographer_frameCallback callback;
    AChoreographer_frameCallback64 callback64;
    AChoreographer_frameCallback64 callback64;
    AChoreographer_vsyncCallback vsyncCallback;
    AChoreographer_vsyncCallback vsyncCallback;
    void* data;
    void* data;
    nsecs_t dueTime;
    nsecs_t dueTime;
    CallbackType callbackType;


    inline bool operator<(const FrameCallback& rhs) const {
    inline bool operator<(const FrameCallback& rhs) const {
        // Note that this is intentionally flipped because we want callbacks due sooner to be at
        // Note that this is intentionally flipped because we want callbacks due sooner to be at
@@ -78,7 +84,7 @@ public:
    void postFrameCallbackDelayed(AChoreographer_frameCallback cb,
    void postFrameCallbackDelayed(AChoreographer_frameCallback cb,
                                  AChoreographer_frameCallback64 cb64,
                                  AChoreographer_frameCallback64 cb64,
                                  AChoreographer_vsyncCallback vsyncCallback, void* data,
                                  AChoreographer_vsyncCallback vsyncCallback, void* data,
                                  nsecs_t delay);
                                  nsecs_t delay, CallbackType callbackType);
    void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data)
    void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data)
            EXCLUDES(gChoreographers.lock);
            EXCLUDES(gChoreographers.lock);
    void unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data);
    void unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data);
@@ -109,6 +115,8 @@ private:


    void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
    void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
                       VsyncEventData vsyncEventData) override;
                       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 dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
    void dispatchHotplugConnectionError(nsecs_t timestamp, int32_t connectionError) override;
    void dispatchHotplugConnectionError(nsecs_t timestamp, int32_t connectionError) override;
    void dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t modeId,
    void dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t modeId,
+2 −0
Original line number Original line Diff line number Diff line
@@ -30,6 +30,7 @@ cc_test {
        "BLASTBufferQueue_test.cpp",
        "BLASTBufferQueue_test.cpp",
        "BufferItemConsumer_test.cpp",
        "BufferItemConsumer_test.cpp",
        "BufferQueue_test.cpp",
        "BufferQueue_test.cpp",
        "Choreographer_test.cpp",
        "CompositorTiming_test.cpp",
        "CompositorTiming_test.cpp",
        "CpuConsumer_test.cpp",
        "CpuConsumer_test.cpp",
        "EndToEndNativeInputTest.cpp",
        "EndToEndNativeInputTest.cpp",
@@ -61,6 +62,7 @@ cc_test {
        "libSurfaceFlingerProp",
        "libSurfaceFlingerProp",
        "libGLESv1_CM",
        "libGLESv1_CM",
        "libinput",
        "libinput",
        "libnativedisplay",
    ],
    ],


    static_libs: [
    static_libs: [
+88 −0
Original line number Original line 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
+7 −5
Original line number Original line Diff line number Diff line
@@ -148,29 +148,31 @@ AChoreographer* AChoreographer_getInstance() {
void AChoreographer_postFrameCallback(AChoreographer* choreographer,
void AChoreographer_postFrameCallback(AChoreographer* choreographer,
                                      AChoreographer_frameCallback callback, void* data) {
                                      AChoreographer_frameCallback callback, void* data) {
    AChoreographer_to_Choreographer(choreographer)
    AChoreographer_to_Choreographer(choreographer)
            ->postFrameCallbackDelayed(callback, nullptr, nullptr, data, 0);
            ->postFrameCallbackDelayed(callback, nullptr, nullptr, data, 0, CALLBACK_ANIMATION);
}
}
void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer,
void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer,
                                             AChoreographer_frameCallback callback, void* data,
                                             AChoreographer_frameCallback callback, void* data,
                                             long delayMillis) {
                                             long delayMillis) {
    AChoreographer_to_Choreographer(choreographer)
    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,
void AChoreographer_postVsyncCallback(AChoreographer* choreographer,
                                      AChoreographer_vsyncCallback callback, void* data) {
                                      AChoreographer_vsyncCallback callback, void* data) {
    AChoreographer_to_Choreographer(choreographer)
    AChoreographer_to_Choreographer(choreographer)
            ->postFrameCallbackDelayed(nullptr, nullptr, callback, data, 0);
            ->postFrameCallbackDelayed(nullptr, nullptr, callback, data, 0, CALLBACK_ANIMATION);
}
}
void AChoreographer_postFrameCallback64(AChoreographer* choreographer,
void AChoreographer_postFrameCallback64(AChoreographer* choreographer,
                                        AChoreographer_frameCallback64 callback, void* data) {
                                        AChoreographer_frameCallback64 callback, void* data) {
    AChoreographer_to_Choreographer(choreographer)
    AChoreographer_to_Choreographer(choreographer)
            ->postFrameCallbackDelayed(nullptr, callback, nullptr, data, 0);
            ->postFrameCallbackDelayed(nullptr, callback, nullptr, data, 0, CALLBACK_ANIMATION);
}
}
void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer,
void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer,
                                               AChoreographer_frameCallback64 callback, void* data,
                                               AChoreographer_frameCallback64 callback, void* data,
                                               uint32_t delayMillis) {
                                               uint32_t delayMillis) {
    AChoreographer_to_Choreographer(choreographer)
    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,
void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer,
                                                AChoreographer_refreshRateCallback callback,
                                                AChoreographer_refreshRateCallback callback,