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 }