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 <nativehelper/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 32using 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 42static bool 43extensionEqual(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 58static const GLubyte* 59nextExtension(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 70static bool 71checkForExtension(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 82bool 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 **/ 101class 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 */ 117 explicit 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 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 */ 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 */ 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 */ 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 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 225PerfMeasurementContext* getContext(jlong context) { 226 return reinterpret_cast<PerfMeasurementContext*>(context); 227} 228 229extern "C" { 230 231static jlong PerfMeasurement_nativeCreateContext(JNIEnv* env, jobject thiz, 232 jint maxQueryCount) { 233 PerfMeasurementContext *context = new PerfMeasurementContext(maxQueryCount); 234 return reinterpret_cast<jlong>(context); 235} 236 237static void PerfMeasurement_nativeDeleteContext(JNIEnv* env, jobject thiz, 238 jlong contextHandle) { 239 PerfMeasurementContext *context = getContext(contextHandle); 240 delete(context); 241} 242 243static jboolean PerfMeasurement_nativeQuerySupport(JNIEnv* env, jobject thiz) { 244 bool supported = PerfMeasurementContext::isMeasurementSupported(); 245 checkGlError(env); 246 return static_cast<jboolean>(supported); 247} 248 249static 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 274static 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 294static 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 305static const 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 328int 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