1 /*
2 * Copyright (C) 2014 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 #define LOG_TAG "Camera2-Legacy-PerfMeasurement-JNI"
18 #include <utils/Log.h>
19 #include <utils/Errors.h>
20 #include <utils/Trace.h>
21 #include <utils/Vector.h>
22
23 #include "jni.h"
24 #include "JNIHelp.h"
25 #include "core_jni_helpers.h"
26
27 #include <ui/GraphicBuffer.h>
28 #include <system/window.h>
29 #include <GLES2/gl2.h>
30 #include <GLES2/gl2ext.h>
31
32 using namespace android;
33
34 // fully-qualified class name
35 #define PERF_MEASUREMENT_CLASS_NAME "android/hardware/camera2/legacy/PerfMeasurement"
36
37 /** GL utility methods copied from com_google_android_gles_jni_GLImpl.cpp */
38
39 // Check if the extension at the head of pExtensions is pExtension. Note that pExtensions is
40 // terminated by either 0 or space, while pExtension is terminated by 0.
41
42 static bool
extensionEqual(const GLubyte * pExtensions,const GLubyte * pExtension)43 extensionEqual(const GLubyte* pExtensions, const GLubyte* pExtension) {
44 while (true) {
45 char a = *pExtensions++;
46 char b = *pExtension++;
47 bool aEnd = a == '\0' || a == ' ';
48 bool bEnd = b == '\0';
49 if (aEnd || bEnd) {
50 return aEnd == bEnd;
51 }
52 if (a != b) {
53 return false;
54 }
55 }
56 }
57
58 static const GLubyte*
nextExtension(const GLubyte * pExtensions)59 nextExtension(const GLubyte* pExtensions) {
60 while (true) {
61 char a = *pExtensions++;
62 if (a == '\0') {
63 return pExtensions-1;
64 } else if ( a == ' ') {
65 return pExtensions;
66 }
67 }
68 }
69
70 static bool
checkForExtension(const GLubyte * pExtensions,const GLubyte * pExtension)71 checkForExtension(const GLubyte* pExtensions, const GLubyte* pExtension) {
72 for (; *pExtensions != '\0'; pExtensions = nextExtension(pExtensions)) {
73 if (extensionEqual(pExtensions, pExtension)) {
74 return true;
75 }
76 }
77 return false;
78 }
79
80 /** End copied GL utility methods */
81
checkGlError(JNIEnv * env)82 bool checkGlError(JNIEnv* env) {
83 int error;
84 if ((error = glGetError()) != GL_NO_ERROR) {
85 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
86 "GLES20 error: 0x%d", error);
87 return true;
88 }
89 return false;
90 }
91
92 /**
93 * Asynchronous low-overhead GL performance measurement using
94 * http://www.khronos.org/registry/gles/extensions/EXT/EXT_disjoint_timer_query.txt
95 *
96 * Measures the duration of GPU processing for a set of GL commands, delivering
97 * the measurement asynchronously once processing completes.
98 *
99 * All calls must come from a single thread with a valid GL context active.
100 **/
101 class PerfMeasurementContext {
102 private:
103 Vector<GLuint> mTimingQueries;
104 size_t mTimingStartIndex;
105 size_t mTimingEndIndex;
106 size_t mTimingQueryIndex;
107 size_t mFreeQueries;
108
109 bool mInitDone;
110 public:
111
112 /**
113 * maxQueryCount should be a conservative estimate of how many query objects
114 * will be active at once, which is a function of the GPU's level of
115 * pipelining and the frequency of queries.
116 */
PerfMeasurementContext(size_t maxQueryCount)117 PerfMeasurementContext(size_t maxQueryCount):
118 mTimingStartIndex(0),
119 mTimingEndIndex(0),
120 mTimingQueryIndex(0) {
121 mTimingQueries.resize(maxQueryCount);
122 mFreeQueries = maxQueryCount;
123 mInitDone = false;
124 }
125
getMaxQueryCount()126 int getMaxQueryCount() {
127 return mTimingQueries.size();
128 }
129
130 /**
131 * Start a measurement period using the next available query object.
132 * Returns INVALID_OPERATION if called multiple times in a row,
133 * and BAD_VALUE if no more query objects are available.
134 */
startGlTimer()135 int startGlTimer() {
136 // Lazy init of queries to avoid needing GL context during construction
137 if (!mInitDone) {
138 glGenQueriesEXT(mTimingQueries.size(), mTimingQueries.editArray());
139 mInitDone = true;
140 }
141
142 if (mTimingEndIndex != mTimingStartIndex) {
143 return INVALID_OPERATION;
144 }
145
146 if (mFreeQueries == 0) {
147 return BAD_VALUE;
148 }
149
150 glBeginQueryEXT(GL_TIME_ELAPSED_EXT, mTimingQueries[mTimingStartIndex]);
151
152 mTimingStartIndex = (mTimingStartIndex + 1) % mTimingQueries.size();
153 mFreeQueries--;
154
155 return OK;
156 }
157
158 /**
159 * Finish the current measurement period
160 * Returns INVALID_OPERATION if called before any startGLTimer calls
161 * or if called multiple times in a row.
162 */
stopGlTimer()163 int stopGlTimer() {
164 size_t nextEndIndex = (mTimingEndIndex + 1) % mTimingQueries.size();
165 if (nextEndIndex != mTimingStartIndex) {
166 return INVALID_OPERATION;
167 }
168 glEndQueryEXT(GL_TIME_ELAPSED_EXT);
169
170 mTimingEndIndex = nextEndIndex;
171
172 return OK;
173 }
174
175 static const nsecs_t NO_DURATION_YET = -1L;
176 static const nsecs_t FAILED_MEASUREMENT = -2L;
177
178 /**
179 * Get the next available duration measurement.
180 *
181 * Returns NO_DURATION_YET if no new measurement is available,
182 * and FAILED_MEASUREMENT if an error occurred during the next
183 * measurement period.
184 *
185 * Otherwise returns a positive number of nanoseconds measuring the
186 * duration of the oldest completed query.
187 */
getNextGlDuration()188 nsecs_t getNextGlDuration() {
189 if (!mInitDone) {
190 // No start/stop called yet
191 return NO_DURATION_YET;
192 }
193
194 GLint available;
195 glGetQueryObjectivEXT(mTimingQueries[mTimingQueryIndex],
196 GL_QUERY_RESULT_AVAILABLE_EXT, &available);
197 if (!available) {
198 return NO_DURATION_YET;
199 }
200
201 GLint64 duration = FAILED_MEASUREMENT;
202 GLint disjointOccurred;
203 glGetIntegerv(GL_GPU_DISJOINT_EXT, &disjointOccurred);
204
205 if (!disjointOccurred) {
206 glGetQueryObjecti64vEXT(mTimingQueries[mTimingQueryIndex],
207 GL_QUERY_RESULT_EXT,
208 &duration);
209 }
210
211 mTimingQueryIndex = (mTimingQueryIndex + 1) % mTimingQueries.size();
212 mFreeQueries++;
213
214 return static_cast<nsecs_t>(duration);
215 }
216
isMeasurementSupported()217 static bool isMeasurementSupported() {
218 const GLubyte* extensions = glGetString(GL_EXTENSIONS);
219 return checkForExtension(extensions,
220 reinterpret_cast<const GLubyte*>("GL_EXT_disjoint_timer_query"));
221 }
222
223 };
224
getContext(jlong context)225 PerfMeasurementContext* getContext(jlong context) {
226 return reinterpret_cast<PerfMeasurementContext*>(context);
227 }
228
229 extern "C" {
230
PerfMeasurement_nativeCreateContext(JNIEnv * env,jobject thiz,jint maxQueryCount)231 static jlong PerfMeasurement_nativeCreateContext(JNIEnv* env, jobject thiz,
232 jint maxQueryCount) {
233 PerfMeasurementContext *context = new PerfMeasurementContext(maxQueryCount);
234 return reinterpret_cast<jlong>(context);
235 }
236
PerfMeasurement_nativeDeleteContext(JNIEnv * env,jobject thiz,jlong contextHandle)237 static void PerfMeasurement_nativeDeleteContext(JNIEnv* env, jobject thiz,
238 jlong contextHandle) {
239 PerfMeasurementContext *context = getContext(contextHandle);
240 delete(context);
241 }
242
PerfMeasurement_nativeQuerySupport(JNIEnv * env,jobject thiz)243 static jboolean PerfMeasurement_nativeQuerySupport(JNIEnv* env, jobject thiz) {
244 bool supported = PerfMeasurementContext::isMeasurementSupported();
245 checkGlError(env);
246 return static_cast<jboolean>(supported);
247 }
248
PerfMeasurement_nativeStartGlTimer(JNIEnv * env,jobject thiz,jlong contextHandle)249 static void PerfMeasurement_nativeStartGlTimer(JNIEnv* env, jobject thiz,
250 jlong contextHandle) {
251
252 PerfMeasurementContext *context = getContext(contextHandle);
253 status_t err = context->startGlTimer();
254 if (err != OK) {
255 switch (err) {
256 case INVALID_OPERATION:
257 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
258 "Mismatched start/end GL timing calls");
259 return;
260 case BAD_VALUE:
261 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
262 "Too many timing queries in progress, max %d",
263 context->getMaxQueryCount());
264 return;
265 default:
266 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
267 "Unknown error starting GL timing");
268 return;
269 }
270 }
271 checkGlError(env);
272 }
273
PerfMeasurement_nativeStopGlTimer(JNIEnv * env,jobject thiz,jlong contextHandle)274 static void PerfMeasurement_nativeStopGlTimer(JNIEnv* env, jobject thiz,
275 jlong contextHandle) {
276
277 PerfMeasurementContext *context = getContext(contextHandle);
278 status_t err = context->stopGlTimer();
279 if (err != OK) {
280 switch (err) {
281 case INVALID_OPERATION:
282 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
283 "Mismatched start/end GL timing calls");
284 return;
285 default:
286 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
287 "Unknown error ending GL timing");
288 return;
289 }
290 }
291 checkGlError(env);
292 }
293
PerfMeasurement_nativeGetNextGlDuration(JNIEnv * env,jobject thiz,jlong contextHandle)294 static jlong PerfMeasurement_nativeGetNextGlDuration(JNIEnv* env,
295 jobject thiz, jlong contextHandle) {
296 PerfMeasurementContext *context = getContext(contextHandle);
297 nsecs_t duration = context->getNextGlDuration();
298
299 checkGlError(env);
300 return static_cast<jlong>(duration);
301 }
302
303 } // extern "C"
304
305 static JNINativeMethod gPerfMeasurementMethods[] = {
306 { "nativeCreateContext",
307 "(I)J",
308 (jlong *)PerfMeasurement_nativeCreateContext },
309 { "nativeDeleteContext",
310 "(J)V",
311 (void *)PerfMeasurement_nativeDeleteContext },
312 { "nativeQuerySupport",
313 "()Z",
314 (jboolean *)PerfMeasurement_nativeQuerySupport },
315 { "nativeStartGlTimer",
316 "(J)V",
317 (void *)PerfMeasurement_nativeStartGlTimer },
318 { "nativeStopGlTimer",
319 "(J)V",
320 (void *)PerfMeasurement_nativeStopGlTimer },
321 { "nativeGetNextGlDuration",
322 "(J)J",
323 (jlong *)PerfMeasurement_nativeGetNextGlDuration }
324 };
325
326
327 // Get all the required offsets in java class and register native functions
register_android_hardware_camera2_legacy_PerfMeasurement(JNIEnv * env)328 int register_android_hardware_camera2_legacy_PerfMeasurement(JNIEnv* env)
329 {
330 // Register native functions
331 return RegisterMethodsOrDie(env,
332 PERF_MEASUREMENT_CLASS_NAME,
333 gPerfMeasurementMethods,
334 NELEM(gPerfMeasurementMethods));
335 }
336