1 /*
2  * Copyright (C) 2018 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 <renderer.h>
19 #include <common.h>
20 #include <jni.h>
21 #include <EGL/egl.h>
22 #include <EGL/eglext.h>
23 #include <android/native_window_jni.h>
24 
25 #include <vector>
26 #include <deque>
27 
28 namespace {
29 
30 using eglGetNextFrameIdANDROID_type = EGLBoolean (*)(EGLDisplay, EGLSurface, EGLuint64KHR *);
31 using eglGetFrameTimestampsANDROID_type = EGLBoolean (*)(EGLDisplay, EGLSurface, EGLuint64KHR, EGLint, const EGLint *, EGLnsecsANDROID *);
32 
33 eglGetNextFrameIdANDROID_type eglGetNextFrameIdANDROID = nullptr;
34 eglGetFrameTimestampsANDROID_type eglGetFrameTimestampsANDROID = nullptr;
35 
36 const int MAX_FRAMES = 240;
37 android::gamecore::Renderer* renderer = nullptr;
38 std::deque<EGLnsecsANDROID> frameReadyTime;
39 std::deque<EGLnsecsANDROID> latchTime;
40 std::deque<EGLuint64KHR> frameIds;
41 
42 } // end of anonymous namespace
43 
44 extern "C"
45 JNIEXPORT void JNICALL
Java_com_android_game_qualification_tests_SurfaceFlingerTestActivity_initDisplay(JNIEnv * env,jobject,jobject surface)46 Java_com_android_game_qualification_tests_SurfaceFlingerTestActivity_initDisplay(JNIEnv* env, jobject, jobject surface) {
47     eglGetNextFrameIdANDROID =
48             reinterpret_cast<eglGetNextFrameIdANDROID_type>(
49                     eglGetProcAddress("eglGetNextFrameIdANDROID"));
50     if (eglGetNextFrameIdANDROID == nullptr) {
51         LOGE("Failed to load eglGetNextFrameIdANDROID");
52         return;
53     }
54     eglGetFrameTimestampsANDROID =
55             reinterpret_cast<eglGetFrameTimestampsANDROID_type>(
56                     eglGetProcAddress("eglGetFrameTimestampsANDROID"));
57     if (eglGetNextFrameIdANDROID == nullptr) {
58         LOGE("Failed to load eglGetFrameTimestampsANDROID");
59         return;
60     }
61 
62     if (renderer == nullptr) {
63         // Create a renderer that draws many circles to overload the GPU.
64         renderer = new android::gamecore::Renderer(1500);
65     }
66     ANativeWindow* window = ANativeWindow_fromSurface(env, surface);
67     renderer->initDisplay(window);
68 }
69 
70 extern "C"
71 JNIEXPORT void JNICALL
Java_com_android_game_qualification_tests_SurfaceFlingerTestActivity_drawFrame(JNIEnv *,jobject)72 Java_com_android_game_qualification_tests_SurfaceFlingerTestActivity_drawFrame(JNIEnv*, jobject) {
73     if (eglGetNextFrameIdANDROID == nullptr || eglGetFrameTimestampsANDROID == nullptr) {
74         return;
75     }
76     EGLuint64KHR frameId;
77     EGLBoolean rc = eglGetNextFrameIdANDROID(renderer->egl.display, renderer->egl.surface, &frameId);
78     if (rc == EGL_TRUE) {
79         frameIds.push_back(frameId);
80     }
81     renderer->update();
82     renderer->draw();
83 
84     static const EGLint timestamps[] = {
85             EGL_RENDERING_COMPLETE_TIME_ANDROID,
86             EGL_COMPOSITION_LATCH_TIME_ANDROID};
87 
88     while(!frameIds.empty()) {
89         EGLuint64KHR fid = *frameIds.begin();
90         EGLnsecsANDROID values[2];
91         rc = eglGetFrameTimestampsANDROID(
92                 renderer->egl.display, renderer->egl.surface, fid, 2, timestamps, values);
93 
94         // Timestamps pending, will try again next frame.
95         if (values[0] == EGL_TIMESTAMP_PENDING_ANDROID
96                 || values[1] == EGL_TIMESTAMP_PENDING_ANDROID) {
97             break;
98         }
99         frameIds.pop_front();
100         if (rc == EGL_TRUE) {
101             if (frameReadyTime.size() == MAX_FRAMES) {
102                 frameReadyTime.pop_front();
103                 latchTime.pop_front();
104             }
105             frameReadyTime.push_back(values[0]);
106             latchTime.push_back(values[1]);
107         } else {
108             LOGE("Unable to retrieve frame data for frame %" PRIu64, fid);
109         }
110     }
111 }
112 
113 
114 /**
115  * Return the oldest available frame data.
116  */
117 extern "C"
118 JNIEXPORT jlongArray JNICALL
Java_com_android_game_qualification_tests_SurfaceFlingerTestActivity_getFrameData(JNIEnv * env,jobject)119 Java_com_android_game_qualification_tests_SurfaceFlingerTestActivity_getFrameData(JNIEnv* env, jobject) {
120     if (frameReadyTime.empty()) {
121         return nullptr;
122     }
123     jlong buffer[] = { *frameReadyTime.begin(), *latchTime.begin() };
124     jlongArray result = env->NewLongArray(2);
125     env->SetLongArrayRegion(result, 0, 2, buffer);
126     frameReadyTime.pop_front();
127     latchTime.pop_front();
128     return result;
129 }
130 
131 
132