/* * Copyright (C) 2021 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. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std::chrono_literals; static constexpr std::chrono::nanoseconds NOMINAL_VSYNC_PERIOD{16ms}; static constexpr std::chrono::nanoseconds DELAY_PERIOD{NOMINAL_VSYNC_PERIOD * 5}; static constexpr std::chrono::nanoseconds ZERO{std::chrono::nanoseconds::zero()}; #define NANOS_PER_SECOND 1000000000LL static int64_t systemTime() { struct timespec time; int result = clock_gettime(CLOCK_MONOTONIC, &time); if (result < 0) { return -errno; } return (time.tv_sec * NANOS_PER_SECOND) + time.tv_nsec; } static std::mutex gLock; struct Callback { Callback(const char* name) : name(name) {} std::string name; int count{0}; std::chrono::nanoseconds frameTime{0LL}; }; struct VsyncCallback : Callback { VsyncCallback(const char* name, JNIEnv* env) : Callback(name), env(env) {} struct FrameTime { FrameTime(const AChoreographerFrameCallbackData* callbackData, int index) : vsyncId(AChoreographerFrameCallbackData_getFrameTimelineVsyncId(callbackData, index)), expectedPresentTime( AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentationTimeNanos( callbackData, index)), deadline(AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos(callbackData, index)) {} const AVsyncId vsyncId{-1}; const int64_t expectedPresentTime{-1}; const int64_t deadline{-1}; }; void populate(const AChoreographerFrameCallbackData* callbackData) { size_t index = AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex(callbackData); preferredFrameTimelineIndex = index; size_t length = AChoreographerFrameCallbackData_getFrameTimelinesLength(callbackData); ASSERT(index < length, "Preferred frame timeline index out of bounds"); timelines.reserve(length); for (int i = 0; i < length; i++) { timelines.push_back(FrameTime(callbackData, i)); } } size_t getPreferredFrameTimelineIndex() const { return preferredFrameTimelineIndex; } const std::vector& getTimeline() const { return timelines; } private: JNIEnv* env; size_t preferredFrameTimelineIndex{std::numeric_limits::max()}; std::vector timelines; }; static void vsyncCallback(int64_t frameTimeNanos, void* data) { std::lock_guard _l(gLock); ATrace_beginSection("vsyncCallback base"); Callback* cb = static_cast(data); cb->count++; cb->frameTime = std::chrono::nanoseconds{frameTimeNanos}; ATrace_endSection(); } static void vsyncCallback(const AChoreographerFrameCallbackData* callbackData, void* data) { ATrace_beginSection("vsyncCallback"); vsyncCallback(AChoreographerFrameCallbackData_getFrameTimeNanos(callbackData), data); VsyncCallback* cb = static_cast(data); cb->populate(callbackData); ATrace_endSection(); } static void frameCallback64(int64_t frameTimeNanos, void* data) { vsyncCallback(frameTimeNanos, data); } static void frameCallback(long frameTimeNanos, void* data) { vsyncCallback((int64_t)frameTimeNanos, data); } static std::chrono::nanoseconds now() { return std::chrono::steady_clock::now().time_since_epoch(); } static void verifyCallback(JNIEnv* env, const Callback& cb, int expectedCount, std::chrono::nanoseconds startTime, std::chrono::nanoseconds maxTime) { std::lock_guard _l{gLock}; ASSERT(cb.count == expectedCount, "Choreographer failed to invoke '%s' %d times - actual: %d", cb.name.c_str(), expectedCount, cb.count); if (maxTime > ZERO) { ASSERT(cb.frameTime - startTime < maxTime, "Callback '%s' has incorrect frame time in invocation %d", cb.name.c_str(), expectedCount); } }