1 /*
2  * Copyright (C) 2010 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 <jni.h>
19 #include <chrono>
20 #include <cstdlib>
21 #include <android/choreographer.h>
22 #include <android/looper.h>
23 #include <android/native_window.h>
24 #include <android/native_window_jni.h>
25 #include <vector>
26 #include <cmath>
27 #include <EGL/egl.h>
28 #include <EGL/eglext.h>
29 #include <GLES2/gl2.h>
30 #include <GLES2/gl2ext.h>
31 
32 #include <android/log.h>
33 #define LOG(...) __android_log_print(ANDROID_LOG_ERROR, "CHOREO-TEST", __VA_ARGS__)
34 
35 using namespace std;
36 
37 static ANativeWindow *theWindow = nullptr;
38 
39 static EGLConfig eglConf;
40 static EGLSurface eglSurface;
41 static EGLContext eglCtx;
42 static EGLDisplay eglDisp;
43 
setupEGL(int w,int h)44 static bool setupEGL(int w, int h) {
45     const EGLint confAttr[] = {
46             EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
47             EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
48             EGL_RED_SIZE,   8,
49             EGL_GREEN_SIZE, 8,
50             EGL_BLUE_SIZE,  8,
51             EGL_ALPHA_SIZE, 8,
52             EGL_DEPTH_SIZE, 16,
53             EGL_NONE
54     };
55 
56     const EGLint ctxAttr[] = {
57             EGL_CONTEXT_CLIENT_VERSION, 2,
58             EGL_NONE
59     };
60 
61     EGLint eglMajVers, eglMinVers;
62     EGLint numConfigs;
63     EGLint format;
64 
65     eglDisp = eglGetDisplay(EGL_DEFAULT_DISPLAY);
66     if (eglDisp == EGL_NO_DISPLAY) {
67         return false;
68     }
69 
70     if (!eglInitialize(eglDisp, &eglMajVers, &eglMinVers)) {
71         return false;
72     }
73 
74     if (!eglChooseConfig(eglDisp, confAttr, &eglConf, 1, &numConfigs)) {
75         return false;
76     }
77 
78     if (!eglGetConfigAttrib(eglDisp, eglConf, EGL_NATIVE_VISUAL_ID, &format)) {
79         return false;
80     }
81 
82     ANativeWindow_setBuffersGeometry(theWindow, 0, 0, format);
83 
84 
85     eglSurface = eglCreateWindowSurface(eglDisp, eglConf, theWindow, 0);
86 
87     if (eglSurface == EGL_NO_SURFACE) {
88         return false;
89     }
90 
91     eglCtx = eglCreateContext(eglDisp, eglConf, EGL_NO_CONTEXT, ctxAttr);
92 
93     if (eglCtx == EGL_NO_CONTEXT) {
94         return false;
95     }
96 
97     if (!eglMakeCurrent(eglDisp, eglSurface, eglSurface, eglCtx)) {
98         return false;
99     }
100 
101     glViewport(0, 0, w, h);
102 
103     return true;
104 }
105 
shutdownEGL()106 static void shutdownEGL() {
107     eglMakeCurrent(eglDisp, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
108     eglDestroyContext(eglDisp, eglCtx);
109     eglDestroySurface(eglDisp, eglSurface);
110     eglTerminate(eglDisp);
111 
112     eglDisp = EGL_NO_DISPLAY;
113     eglSurface = EGL_NO_SURFACE;
114     eglCtx = EGL_NO_CONTEXT;
115 
116     if (theWindow) {
117         ANativeWindow_release(theWindow);
118         theWindow = nullptr;
119     }
120 }
121 
renderFrames()122 static void renderFrames() {
123     glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
124     glClear(GL_COLOR_BUFFER_BIT);
125     eglSwapBuffers(eglDisp, eglSurface);
126 }
127 
128 static volatile bool doRender = false;
129 static std::vector<unsigned long> frameTimes;
130 static AChoreographer* choreographer;
131 static ALooper* looper;
132 
frameCallback(long frameTimeNanos,void *)133 static void frameCallback(long frameTimeNanos, void*) {
134     // On 32-bit machine, some frametimes will come out as negative longs
135     frameTimes.push_back((unsigned long)frameTimeNanos);
136     AChoreographer_postFrameCallback(choreographer, frameCallback, nullptr);
137     renderFrames();
138     ALooper_wake(looper);
139 }
140 
141 extern "C"
142 JNIEXPORT bool JNICALL
Java_com_android_game_qualification_tests_ChoreoTestActivity_runTheTest(JNIEnv * env,jobject,jobject surface)143 Java_com_android_game_qualification_tests_ChoreoTestActivity_runTheTest(JNIEnv* env, jobject, jobject surface) {
144     theWindow = ANativeWindow_fromSurface(env, surface);
145     if (!theWindow) {
146         return false;
147     }
148     if (!setupEGL(500,500)) {
149         return false;
150     }
151 
152     looper = ALooper_prepare(0);
153     choreographer = reinterpret_cast<AChoreographer*>(AChoreographer_getInstance());
154 
155     if (choreographer == nullptr) {
156         return false;
157     }
158 
159     AChoreographer_postFrameCallback(choreographer, frameCallback, nullptr);
160 
161     while (doRender && ALooper_pollAll(-1, nullptr, nullptr, nullptr));
162 
163     shutdownEGL();
164 
165     return true;
166 }
167 
168 extern "C"
169 JNIEXPORT void JNICALL
Java_com_android_game_qualification_tests_ChoreoTestActivity_stopTheTest(JNIEnv *,jobject)170 Java_com_android_game_qualification_tests_ChoreoTestActivity_stopTheTest(JNIEnv*, jobject) {
171     doRender = false;
172 }
173 
174 extern "C"
175 JNIEXPORT void JNICALL
Java_com_android_game_qualification_tests_ChoreoTestActivity_startTheTest(JNIEnv *,jobject)176 Java_com_android_game_qualification_tests_ChoreoTestActivity_startTheTest(JNIEnv*, jobject) {
177     doRender = true;
178 }
179 
180 extern "C"
181 JNIEXPORT jobject JNICALL
Java_com_android_game_qualification_tests_ChoreoTestActivity_getFrameIntervals(JNIEnv * env,jobject)182 Java_com_android_game_qualification_tests_ChoreoTestActivity_getFrameIntervals(JNIEnv* env, jobject) {
183     jclass arrayListClass = env->FindClass("java/util/ArrayList");
184     jmethodID arrayListConstructor = env->GetMethodID(arrayListClass, "<init>", "()V");
185     jmethodID addMethod = env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z");
186 
187     jclass longClass = env->FindClass("java/lang/Long");
188     jmethodID longValueOf = env->GetStaticMethodID(longClass, "valueOf", "(J)Ljava/lang/Long;");
189 
190     jobject list = env->NewObject(arrayListClass, arrayListConstructor);
191 
192     // Some early frames might have misleading data.
193     for (int i = 5; i < frameTimes.size(); ++i) {
194         jlong interval = frameTimes[i] - frameTimes[i-1];
195 
196         jobject javaInterval = env->CallStaticObjectMethod(longClass, longValueOf, interval);
197         env->CallBooleanMethod(list, addMethod, javaInterval);
198     }
199 
200     return list;
201 }