Home | History | Annotate | Download | only in jni
      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 
     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
     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*
     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
     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 
     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      */
    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 
    225 PerfMeasurementContext* getContext(jlong context) {
    226     return reinterpret_cast<PerfMeasurementContext*>(context);
    227 }
    228 
    229 extern "C" {
    230 
    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 
    237 static void PerfMeasurement_nativeDeleteContext(JNIEnv* env, jobject thiz,
    238         jlong contextHandle) {
    239     PerfMeasurementContext *context = getContext(contextHandle);
    240     delete(context);
    241 }
    242 
    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 
    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 
    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 
    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 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
    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