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