1 /*
2 * Copyright 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18 #include <android/choreographer.h>
19 #include <android/log.h>
20 #include <android/surface_control_jni.h>
21 #include <jni.h>
22 #include <private/surface_control_private.h>
23 #include <time.h>
24 #include <utils/Log.h>
25 #include <utils/Mutex.h>
26
27 #include <chrono>
28 #include <cmath>
29 #include <condition_variable>
30 #include <mutex>
31 #include <thread>
32
33 #undef LOG_TAG
34 #define LOG_TAG "AttachedChoreographerNativeTest"
35
36 // Copied from cts/tests/tests/view/jni/jniAssert.h, to be removed when integrated in CTS.
37 #define ASSERT(condition, format, args...) \
38 if (!(condition)) { \
39 fail(env, format, ##args); \
40 return; \
41 }
42
43 using namespace std::chrono_literals;
44
45 static constexpr std::chrono::nanoseconds kMaxRuntime{1s};
46 static constexpr float kFpsTolerance = 5.0f;
47
48 static constexpr int kNumOfFrames = 20;
49
50 struct {
51 struct {
52 jclass clazz;
53 jmethodID endTest;
54 } attachedChoreographerNativeTest;
55 } gJni;
56
57 struct CallbackData {
58 std::mutex mutex;
59
60 // Condition to signal callbacks are done running and test can be verified.
61 std::condition_variable_any condition;
62
63 // Flag to ensure not to lock on the condition if notify is called before wait_for.
64 bool callbacksComplete = false;
65
66 AChoreographer* choreographer = nullptr;
GUARDED_BYCallbackData67 int count GUARDED_BY(mutex){0};
GUARDED_BYCallbackData68 std::chrono::nanoseconds frameTime GUARDED_BY(mutex){0};
69 std::chrono::nanoseconds startTime;
GUARDED_BYCallbackData70 std::chrono::nanoseconds endTime GUARDED_BY(mutex){0};
71 };
72
now()73 static std::chrono::nanoseconds now() {
74 return std::chrono::steady_clock::now().time_since_epoch();
75 }
76
vsyncCallback(const AChoreographerFrameCallbackData * callbackData,void * data)77 static void vsyncCallback(const AChoreographerFrameCallbackData* callbackData, void* data) {
78 ALOGI("%s: Vsync callback running", __func__);
79 long frameTimeNanos = AChoreographerFrameCallbackData_getFrameTimeNanos(callbackData);
80
81 auto* cb = static_cast<CallbackData*>(data);
82 {
83 std::lock_guard<std::mutex> _l(cb->mutex);
84 cb->count++;
85 cb->endTime = now();
86 cb->frameTime = std::chrono::nanoseconds{frameTimeNanos};
87
88 ALOGI("%s: ran callback now %ld, frameTimeNanos %ld, new count %d", __func__,
89 static_cast<long>(cb->endTime.count()), frameTimeNanos, cb->count);
90 if (cb->endTime - cb->startTime > kMaxRuntime) {
91 cb->callbacksComplete = true;
92 cb->condition.notify_all();
93 return;
94 }
95 }
96
97 ALOGI("%s: Posting next callback", __func__);
98 AChoreographer_postVsyncCallback(cb->choreographer, vsyncCallback, data);
99 }
100
fail(JNIEnv * env,const char * format,...)101 static void fail(JNIEnv* env, const char* format, ...) {
102 va_list args;
103
104 va_start(args, format);
105 char* msg;
106 int rc = vasprintf(&msg, format, args);
107 va_end(args);
108
109 jclass exClass;
110 const char* className = "java/lang/AssertionError";
111 exClass = env->FindClass(className);
112 env->ThrowNew(exClass, msg);
113 free(msg);
114 }
115
SurfaceControl_getChoreographer(JNIEnv * env,jclass,jobject surfaceControlObj)116 jlong SurfaceControl_getChoreographer(JNIEnv* env, jclass, jobject surfaceControlObj) {
117 return reinterpret_cast<jlong>(
118 ASurfaceControl_getChoreographer(ASurfaceControl_fromJava(env, surfaceControlObj)));
119 }
120
frameRateEquals(float fr1,float fr2)121 static bool frameRateEquals(float fr1, float fr2) {
122 return std::abs(fr1 - fr2) <= kFpsTolerance;
123 }
124
endTest(JNIEnv * env,jobject clazz)125 static void endTest(JNIEnv* env, jobject clazz) {
126 env->CallVoidMethod(clazz, gJni.attachedChoreographerNativeTest.endTest);
127 }
128
android_view_ChoreographerNativeTest_testPostVsyncCallbackAtFrameRate(JNIEnv * env,jobject clazz,jlong choreographerPtr,jfloat expectedFrameRate)129 static void android_view_ChoreographerNativeTest_testPostVsyncCallbackAtFrameRate(
130 JNIEnv* env, jobject clazz, jlong choreographerPtr, jfloat expectedFrameRate) {
131 AChoreographer* choreographer = reinterpret_cast<AChoreographer*>(choreographerPtr);
132 CallbackData cb;
133 cb.choreographer = choreographer;
134 cb.startTime = now();
135 ALOGI("%s: Post first callback at %ld", __func__, static_cast<long>(cb.startTime.count()));
136 AChoreographer_postVsyncCallback(choreographer, vsyncCallback, &cb);
137
138 std::scoped_lock<std::mutex> conditionLock(cb.mutex);
139 ASSERT(cb.condition.wait_for(cb.mutex, 2 * kMaxRuntime, [&cb] { return cb.callbacksComplete; }),
140 "Never received callbacks!");
141
142 float actualFrameRate = static_cast<float>(cb.count) /
143 (static_cast<double>((cb.endTime - cb.startTime).count()) / 1'000'000'000.0);
144 ALOGI("%s: callback called %d times with final start time %ld, end time %ld, effective "
145 "frame rate %f",
146 __func__, cb.count, static_cast<long>(cb.startTime.count()),
147 static_cast<long>(cb.endTime.count()), actualFrameRate);
148 ASSERT(frameRateEquals(actualFrameRate, expectedFrameRate),
149 "Effective frame rate is %f but expected to be %f", actualFrameRate, expectedFrameRate);
150
151 endTest(env, clazz);
152 }
153
154 static JNINativeMethod gMethods[] = {
155 {"nativeSurfaceControl_getChoreographer", "(Landroid/view/SurfaceControl;)J",
156 (void*)SurfaceControl_getChoreographer},
157 {"nativeTestPostVsyncCallbackAtFrameRate", "(JF)V",
158 (void*)android_view_ChoreographerNativeTest_testPostVsyncCallbackAtFrameRate},
159 };
160
register_android_android_view_tests_ChoreographerNativeTest(JNIEnv * env)161 int register_android_android_view_tests_ChoreographerNativeTest(JNIEnv* env) {
162 jclass clazz =
163 env->FindClass("android/view/choreographertests/AttachedChoreographerNativeTest");
164 gJni.attachedChoreographerNativeTest.clazz = static_cast<jclass>(env->NewGlobalRef(clazz));
165 gJni.attachedChoreographerNativeTest.endTest = env->GetMethodID(clazz, "endTest", "()V");
166 return env->RegisterNatives(clazz, gMethods, sizeof(gMethods) / sizeof(JNINativeMethod));
167 }
168