blob: 45b82d667a73a232c524da76e5d26e6e3cabc388 [file] [log] [blame]
/*
* Copyright (C) 2016 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 <android/choreographer.h>
#include <jni.h>
#include <sys/time.h>
#include <time.h>
#include <chrono>
#include <cstdlib>
#include <cstring>
#include <mutex>
#include <thread>
#define LOG_TAG "ChoreographerNative"
#define ASSERT(condition, format, args...) \
if (!(condition)) { \
fail(env, format, ## args); \
return; \
}
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 std::mutex gLock;
struct Callback {
int count{0};
std::chrono::nanoseconds frameTime{0};
};
static void frameCallback(long frameTimeNanos, void* data) {
std::lock_guard<std::mutex> _l(gLock);
Callback* cb = static_cast<Callback*>(data);
cb->count++;
cb->frameTime = std::chrono::nanoseconds{frameTimeNanos};
}
static std::chrono::nanoseconds now() {
return std::chrono::steady_clock::now().time_since_epoch();
}
static void fail(JNIEnv* env, const char* format, ...) {
va_list args;
va_start(args, format);
char *msg;
int rc = vasprintf(&msg, format, args);
va_end(args);
jclass exClass;
const char *className = "java/lang/AssertionError";
exClass = env->FindClass(className);
env->ThrowNew(exClass, msg);
free(msg);
}
static jlong android_view_cts_ChoreographerNativeTest_getChoreographer(JNIEnv*, jclass) {
std::lock_guard<std::mutex> _l{gLock};
return reinterpret_cast<jlong>(AChoreographer_getInstance());
}
static jboolean android_view_cts_ChoreographerNativeTest_prepareChoreographerTests(JNIEnv*, jclass,
jlong choreographerPtr) {
std::lock_guard<std::mutex> _l{gLock};
AChoreographer* choreographer = reinterpret_cast<AChoreographer*>(choreographerPtr);
return choreographer != nullptr;
}
static void android_view_cts_ChoreographerNativeTest_testPostCallbackWithoutDelayEventuallyRunsCallback(
JNIEnv* env, jclass, jlong choreographerPtr) {
AChoreographer* choreographer = reinterpret_cast<AChoreographer*>(choreographerPtr);
Callback* cb1 = new Callback();
Callback* cb2 = new Callback();
auto start = now();
AChoreographer_postFrameCallback(choreographer, frameCallback, cb1);
AChoreographer_postFrameCallback(choreographer, frameCallback, cb2);
std::this_thread::sleep_for(NOMINAL_VSYNC_PERIOD * 3);
{
std::lock_guard<std::mutex> _l{gLock};
ASSERT(cb1->count == 1, "Choreographer failed to invoke callback 1");
ASSERT(cb1->frameTime - start < NOMINAL_VSYNC_PERIOD * 3,
"Callback 1 has incorect frame time on first invokation");
ASSERT(cb2->count == 1, "Choreographer failed to invoke callback 2");
ASSERT(cb2->frameTime - start < NOMINAL_VSYNC_PERIOD * 3,
"Callback 2 has incorect frame time on first invokation");
auto delta = cb2->frameTime - cb1->frameTime;
ASSERT(delta == delta.zero() || delta > delta.zero() && delta < NOMINAL_VSYNC_PERIOD * 2,
"Callback 1 and 2 have frame times too large of a delta in frame times");
}
AChoreographer_postFrameCallback(choreographer, frameCallback, cb1);
start = now();
std::this_thread::sleep_for(NOMINAL_VSYNC_PERIOD * 3);
{
std::lock_guard<std::mutex> _l{gLock};
ASSERT(cb1->count == 2, "Choreographer failed to invoke callback 1 a second time");
ASSERT(cb1->frameTime - start < NOMINAL_VSYNC_PERIOD * 3,
"Callback 1 has incorect frame time on second invokation");
ASSERT(cb2->count == 1, "Choreographer invoked callback 2 when not posted");
}
}
static void android_view_cts_ChoreographerNativeTest_testPostCallbackWithDelayEventuallyRunsCallback(
JNIEnv* env, jclass, jlong choreographerPtr) {
AChoreographer* choreographer = reinterpret_cast<AChoreographer*>(choreographerPtr);
Callback* cb1 = new Callback();
auto start = now();
auto delay = std::chrono::duration_cast<std::chrono::milliseconds>(DELAY_PERIOD).count();
AChoreographer_postFrameCallbackDelayed(choreographer, frameCallback, cb1, delay);
std::this_thread::sleep_for(NOMINAL_VSYNC_PERIOD * 3);
{
std::lock_guard<std::mutex> _l{gLock};
ASSERT(cb1->count == 0,
"Choreographer failed to delay callback for a sufficient period of time");
}
std::this_thread::sleep_for(DELAY_PERIOD);
{
std::lock_guard<std::mutex> _l{gLock};
ASSERT(cb1->count == 1, "Choreographer failed to invoke delayed callback");
ASSERT(cb1->frameTime - start < DELAY_PERIOD + NOMINAL_VSYNC_PERIOD * 3,
"Frametime on callback is incorrect")
}
}
static JNINativeMethod gMethods[] = {
{ "nativeGetChoreographer", "()J",
(void *) android_view_cts_ChoreographerNativeTest_getChoreographer},
{ "nativePrepareChoreographerTests", "(J)Z",
(void *) android_view_cts_ChoreographerNativeTest_prepareChoreographerTests},
{ "nativeTestPostCallbackWithoutDelayEventuallyRunsCallbacks", "(J)V",
(void *) android_view_cts_ChoreographerNativeTest_testPostCallbackWithoutDelayEventuallyRunsCallback},
{ "nativeTestPostCallbackWithDelayEventuallyRunsCallbacks", "(J)V",
(void *) android_view_cts_ChoreographerNativeTest_testPostCallbackWithDelayEventuallyRunsCallback},
};
int register_android_view_cts_ChoreographerNativeTest(JNIEnv* env)
{
jclass clazz = env->FindClass("android/view/cts/ChoreographerNativeTest");
return env->RegisterNatives(clazz, gMethods,
sizeof(gMethods) / sizeof(JNINativeMethod));
}