diff --git a/android/cpp/jni.cpp b/android/cpp/jni.cpp index a406e83c0..41af8ae3c 100644 --- a/android/cpp/jni.cpp +++ b/android/cpp/jni.cpp @@ -30,6 +30,7 @@ std::string data_path; std::string apk_path; jmethodID on_map_changed_id = nullptr; +jmethodID on_fps_changed_id = nullptr; jclass lon_lat_class = nullptr; jmethodID lon_lat_constructor_id = nullptr; @@ -665,14 +666,14 @@ void JNICALL nativeSetDebug(JNIEnv* env, jobject obj, jlong native_map_view_ptr, mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetDebug"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - COFFEE_TRY_JNI(env, native_map_view->getMap().setDebug(debug)); + COFFEE_TRY_JNI(env, native_map_view->getMap().setDebug(debug); native_map_view->enableFps(debug)); } void JNICALL nativeToggleDebug(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeToggleDebug"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - COFFEE_TRY_JNI(env, native_map_view->getMap().toggleDebug()); + COFFEE_TRY_JNI(env, native_map_view->getMap().toggleDebug(); native_map_view->enableFps(native_map_view->getMap().getDebug())); } jboolean JNICALL nativeGetDebug(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { @@ -773,6 +774,12 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { return JNI_ERR; } + on_fps_changed_id = env->GetMethodID(native_map_view_class, "onFpsChanged", "(D)V"); + if (on_fps_changed_id == nullptr) { + env->ExceptionDescribe(); + return JNI_ERR; + } + runtime_exception_class = env->FindClass("java/lang/RuntimeException"); if (runtime_exception_class == nullptr) { env->ExceptionDescribe(); @@ -951,6 +958,7 @@ extern "C" JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) { lon_lat_zoom_zoom_id = nullptr; on_map_changed_id = nullptr; + on_fps_changed_id = nullptr; env->DeleteGlobalRef(runtime_exception_class); runtime_exception_class = nullptr; diff --git a/android/cpp/native_map_view.cpp b/android/cpp/native_map_view.cpp index de3a7ffca..954ca1cf0 100644 --- a/android/cpp/native_map_view.cpp +++ b/android/cpp/native_map_view.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -72,6 +73,7 @@ void MBGLView::swap() { mbgl::Log::Error(mbgl::Event::OpenGL, "eglSwapBuffers() returned error %d", eglGetError()); } map->swapped(); + nativeView.updateFps(); } else { mbgl::Log::Info(mbgl::Event::Android, "Not swapping as we are not ready"); } @@ -664,5 +666,74 @@ void NativeMapView::notifyMapChange() { env = nullptr; } +void NativeMapView::enableFps(bool enable) { + mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::enableFps()"); + + fps_enabled = enable; +} + +void NativeMapView::updateFps() { + mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::updateFps()"); + + if (!fps_enabled) { + return; + } + + static int frames = 0; + static int64_t time_elapsed = 0LL; + + frames++; + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + int64_t current_time = now.tv_sec*1000000000LL + now.tv_nsec; + + if (current_time - time_elapsed >= 1) { + fps = frames / ((current_time - time_elapsed) / 1E9); + mbgl::Log::Debug(mbgl::Event::Render, "FPS: %4.2f", fps); + time_elapsed = current_time; + frames = 0; + } + + assert(vm != nullptr); + assert(obj != nullptr); + + JavaVMAttachArgs args = { + JNI_VERSION_1_2, + "NativeMapView::updateFps()", + NULL + }; + + jint ret; + JNIEnv* env = nullptr; + bool detach = false; + ret = vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6); + if (ret != JNI_OK) { + if (ret != JNI_EDETACHED) { + mbgl::Log::Error(mbgl::Event::JNI, "GetEnv() failed with %i", ret); + return; + } else { + ret = vm->AttachCurrentThread(&env, &args); + if (ret != JNI_OK) { + mbgl::Log::Error(mbgl::Event::JNI, "AttachCurrentThread() failed with %i", ret); + return; + } + detach = true; + } + } + + env->CallVoidMethod(obj, on_fps_changed_id, fps); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + } + + if (detach) { + if ((ret = vm->DetachCurrentThread()) != JNI_OK) { + mbgl::Log::Error(mbgl::Event::JNI, "DetachCurrentThread() failed with %i", ret); + return; + } + } + env = nullptr; +} + } } diff --git a/android/java/app/src/main/java/com/mapbox/mapboxgl/app/MainActivity.java b/android/java/app/src/main/java/com/mapbox/mapboxgl/app/MainActivity.java index 1baa96db6..d75bded3b 100644 --- a/android/java/app/src/main/java/com/mapbox/mapboxgl/app/MainActivity.java +++ b/android/java/app/src/main/java/com/mapbox/mapboxgl/app/MainActivity.java @@ -15,8 +15,10 @@ import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Spinner; +import android.widget.TextView; import com.mapbox.mapboxgl.lib.LonLatZoom; +import com.mapbox.mapboxgl.lib.MapView; public class MainActivity extends ActionBarActivity { @@ -34,6 +36,9 @@ public class MainActivity extends ActionBarActivity { // Holds the MapFragment MapFragment mMapFragment; + // The FPS label + TextView mFpsTextView; + // // Lifecycle events // @@ -47,6 +52,9 @@ public class MainActivity extends ActionBarActivity { // Load the layout setContentView(R.layout.activity_main); mMapFragment = (MapFragment)getSupportFragmentManager().findFragmentById(R.id.fragment_map); + mMapFragment.getMap().setOnFpsChangedListener(new MyOnFpsChangedListener()); + mFpsTextView = (TextView)findViewById(R.id.view_fps); + mFpsTextView.setText(""); // Add a toolbar as the action bar Toolbar mainToolbar = (Toolbar)findViewById(R.id.toolbar_main); @@ -62,6 +70,10 @@ public class MainActivity extends ActionBarActivity { styleSpinner.setOnItemSelectedListener(new StyleSpinnerListener()); } + // + // Other events + // + // Adds items to the action bar menu @Override public boolean onCreateOptionsMenu(Menu menu) { @@ -70,9 +82,9 @@ public class MainActivity extends ActionBarActivity { return super.onCreateOptionsMenu(menu); } + // Called when pressing action bar items @Override public boolean onOptionsItemSelected(MenuItem item) { - // Handle presses on the action bar items switch (item.getItemId()) { case R.id.action_gps: // Get a GPS position @@ -89,6 +101,14 @@ public class MainActivity extends ActionBarActivity { case R.id.action_debug: // Toggle debug mode mMapFragment.getMap().toggleDebug(); + + // Show the FPS counter + if (mMapFragment.getMap().isDebugActive()) { + mFpsTextView.setVisibility(View.VISIBLE); + mFpsTextView.setText(getResources().getString(R.string.label_fps)); + } else { + mFpsTextView.setVisibility(View.INVISIBLE); + } return true; default: @@ -121,7 +141,7 @@ public class MainActivity extends ActionBarActivity { } } - // This class handles navigation events + // This class handles style change events private class StyleSpinnerListener implements AdapterView.OnItemSelectedListener { @Override @@ -168,4 +188,13 @@ public class MainActivity extends ActionBarActivity { mMapFragment.getMap().setStyleUrl(""); } } + + // Called when FPS changes + public class MyOnFpsChangedListener implements MapView.OnFpsChangedListener { + + @Override + public void onFpsChanged(double fps) { + mFpsTextView.setText(getResources().getString(R.string.label_fps) + String.format(" %4.2f", fps)); + } + } } diff --git a/android/java/app/src/main/res/layout/activity_main.xml b/android/java/app/src/main/res/layout/activity_main.xml index 851e12b43..b69df55de 100644 --- a/android/java/app/src/main/res/layout/activity_main.xml +++ b/android/java/app/src/main/res/layout/activity_main.xml @@ -10,16 +10,33 @@ android:layout_width="match_parent" android:minHeight="?attr/actionBarSize" android:background="?attr/colorPrimary"> + - + android:layout_height="fill_parent" + android:orientation="vertical"> + + + + + + diff --git a/android/java/app/src/main/res/values/strings.xml b/android/java/app/src/main/res/values/strings.xml index fe5385adf..05f821ee2 100644 --- a/android/java/app/src/main/res/values/strings.xml +++ b/android/java/app/src/main/res/values/strings.xml @@ -7,6 +7,8 @@ Toggle debug mode + FPS: + Bright Basic diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java index f72ac95e6..8985c318b 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java @@ -1177,7 +1177,36 @@ public class MapView extends SurfaceView { // Need to update anything that relies on map state protected void onMapChanged() { if (mOnMapChangedListener != null) { - mOnMapChangedListener.onMapChanged(); + post(new Runnable() { + @Override + public void run() { + mOnMapChangedListener.onMapChanged(); + } + }); + } + } + + public interface OnFpsChangedListener { + void onFpsChanged(double fps); + } + + private OnFpsChangedListener mOnFpsChangedListener; + + // Adds a listener for onFpsChanged + public void setOnFpsChangedListener(OnFpsChangedListener listener) { + mOnFpsChangedListener = listener; + } + + // Called when debug mode is enabled to update a FPS counter + // Called via JNI from NativeMapView + protected void onFpsChanged(final double fps) { + if (mOnFpsChangedListener != null) { + post(new Runnable() { + @Override + public void run() { + mOnFpsChangedListener.onFpsChanged(fps); + } + }); } } } diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java index bdea6f7e3..190dee423 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java @@ -387,10 +387,13 @@ class NativeMapView { // protected void onMapChanged() { - //Log.v(TAG, "onMapChanged"); mMapView.onMapChanged(); } + protected void onFpsChanged(double fps) { + mMapView.onFpsChanged(fps); + } + // // JNI methods // diff --git a/include/mbgl/android/jni.hpp b/include/mbgl/android/jni.hpp index 2dbdf2ebe..da3f59dae 100644 --- a/include/mbgl/android/jni.hpp +++ b/include/mbgl/android/jni.hpp @@ -12,6 +12,7 @@ extern std::string data_path; extern std::string apk_path; extern jmethodID on_map_changed_id; +extern jmethodID on_fps_changed_id; extern jclass lon_lat_class; extern jmethodID lon_lat_constructor_id; diff --git a/include/mbgl/android/native_map_view.hpp b/include/mbgl/android/native_map_view.hpp index 8f37fe947..4752d1862 100644 --- a/include/mbgl/android/native_map_view.hpp +++ b/include/mbgl/android/native_map_view.hpp @@ -59,6 +59,9 @@ public: void notifyMapChange(); + void enableFps(bool enable); + void updateFps(); + private: EGLConfig chooseConfig(const EGLConfig configs[], EGLint num_configs); @@ -86,6 +89,9 @@ private: bool first_time = false; bool using_depth24 = false; + + bool fps_enabled = false; + double fps = 0.0; }; }