diff --git a/app/src/main/cpp/main.cpp b/app/src/main/cpp/main.cpp index 18b8f57c..a89c9b28 100644 --- a/app/src/main/cpp/main.cpp +++ b/app/src/main/cpp/main.cpp @@ -11,6 +11,8 @@ bool Halt; jobject Surface; uint FaultCount; skyline::GroupMutex JniMtx; +skyline::u16 fps; +skyline::u32 frametime; void signalHandler(int signal) { syslog(LOG_ERR, "Halting program due to signal: %s", strsignal(signal)); @@ -74,3 +76,11 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_setSurface(JNIEnv * Surface = surface; JniMtx.unlock(); } + +extern "C" JNIEXPORT jint Java_emu_skyline_EmulationActivity_getFps(JNIEnv *env, jobject thiz) { + return fps; +} + +extern "C" JNIEXPORT jfloat Java_emu_skyline_EmulationActivity_getFrametime(JNIEnv *env, jobject thiz) { + return static_cast(frametime) / 100; +} diff --git a/app/src/main/cpp/skyline/gpu.cpp b/app/src/main/cpp/skyline/gpu.cpp index dcc9f529..f0801752 100644 --- a/app/src/main/cpp/skyline/gpu.cpp +++ b/app/src/main/cpp/skyline/gpu.cpp @@ -8,6 +8,8 @@ extern bool Halt; extern jobject Surface; +extern skyline::u16 fps; +extern skyline::u32 frametime; namespace skyline::gpu { GPU::GPU(const DeviceState &state) : state(state), window(ANativeWindow_fromSurface(state.jvm->GetEnv(), Surface)), vsyncEvent(std::make_shared(state)), bufferEvent(std::make_shared(state)) { @@ -57,12 +59,16 @@ namespace skyline::gpu { vsyncEvent->Signal(); texture->releaseCallback(); - if (prevTime != 0) { - auto now = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); - state.logger->Error("{} ms, {} FPS", (now - prevTime) / 1000, 1000000 / (now - prevTime)); - } + if (frameTimestamp) { + auto now = util::GetTimeNs(); - prevTime = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); + frametime = static_cast((now - frameTimestamp) / 10000); // frametime / 100 is the real ms value, this is to retain the first two decimals + fps = static_cast(1000000000 / (now - frameTimestamp)); + + frameTimestamp = now; + } else { + frameTimestamp = util::GetTimeNs(); + } } } } diff --git a/app/src/main/cpp/skyline/gpu.h b/app/src/main/cpp/skyline/gpu.h index 12fbff05..0c052972 100644 --- a/app/src/main/cpp/skyline/gpu.h +++ b/app/src/main/cpp/skyline/gpu.h @@ -20,7 +20,7 @@ namespace skyline::gpu { ANativeWindow *window; //!< The ANativeWindow to render to const DeviceState &state; //!< The state of the device bool surfaceUpdate{}; //!< If the surface needs to be updated - double prevTime{}; //!< The time passed from the last frame + u64 frameTimestamp{}; //!< The timestamp of the last frame being shown public: std::queue> presentationQueue; //!< A queue of all the PresentationTextures to be posted to the display diff --git a/app/src/main/java/emu/skyline/EmulationActivity.kt b/app/src/main/java/emu/skyline/EmulationActivity.kt index 48749c88..1012a925 100644 --- a/app/src/main/java/emu/skyline/EmulationActivity.kt +++ b/app/src/main/java/emu/skyline/EmulationActivity.kt @@ -14,8 +14,9 @@ import android.view.Surface import android.view.SurfaceHolder import android.view.WindowManager import androidx.appcompat.app.AppCompatActivity +import androidx.preference.PreferenceManager import emu.skyline.loader.getRomFormat -import kotlinx.android.synthetic.main.app_activity.* +import kotlinx.android.synthetic.main.emu_activity.* import java.io.File class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback { @@ -78,6 +79,16 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback { */ private external fun setSurface(surface: Surface?) + /** + * This returns the current FPS of the application + */ + private external fun getFps(): Int + + /** + * This returns the current frame-time of the application + */ + private external fun getFrametime(): Float + /** * This executes the specified ROM, [preferenceFd] and [logFd] are assumed to be valid beforehand * @@ -101,12 +112,14 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback { } /** - * This sets up [preferenceFd] and [logFd] then calls [executeApplication] for executing the application + * This makes the window fullscreen then sets up [preferenceFd] and [logFd], sets up the performance statistics and finally calls [executeApplication] for executing the application */ override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.app_activity) + setContentView(R.layout.emu_activity) + + window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN) val preference = File("${applicationInfo.dataDir}/shared_prefs/${applicationInfo.packageName}_preferences.xml") preferenceFd = ParcelFileDescriptor.open(preference, ParcelFileDescriptor.MODE_READ_WRITE) @@ -114,9 +127,21 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback { val log = File("${applicationInfo.dataDir}/skyline.log") logFd = ParcelFileDescriptor.open(log, ParcelFileDescriptor.MODE_CREATE or ParcelFileDescriptor.MODE_READ_WRITE) - window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN) game_view.holder.addCallback(this) + val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this) + + if (sharedPreferences.getBoolean("perf_stats", false)) { + lateinit var perfRunnable: Runnable + + perfRunnable = Runnable { + perf_stats.text = "${getFps()} FPS\n${getFrametime()}ms" + perf_stats.postDelayed(perfRunnable, 250) + } + + perf_stats.postDelayed(perfRunnable, 250) + } + executeApplication(intent.data!!) } diff --git a/app/src/main/res/layout/app_activity.xml b/app/src/main/res/layout/app_activity.xml deleted file mode 100644 index 9436eba1..00000000 --- a/app/src/main/res/layout/app_activity.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - diff --git a/app/src/main/res/layout/emu_activity.xml b/app/src/main/res/layout/emu_activity.xml new file mode 100644 index 00000000..68713c92 --- /dev/null +++ b/app/src/main/res/layout/emu_activity.xml @@ -0,0 +1,25 @@ + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1624f26e..25d95041 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -32,7 +32,7 @@ Log Level Compact Logs Logs will be displayed in a compact form factor - The logs will be displayed in a verbose form factor + Logs will be displayed in a verbose form factor System Use Docked Mode The system will emulate being in handheld mode @@ -59,4 +59,7 @@ We use Kotlin Standard Library for accessing convenience functions in Kotlin (Apache License 2.0) Material Design Icons We use Material Design Icons to have consistent iconography throughout the application + Show Performance Statistics + Performance Statistics will not be shown + Performance Statistics will be shown in the top-left corner diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index e82bdbfb..97b7f264 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -43,6 +43,12 @@ android:summaryOn="@string/select_action_desc_on" app:key="select_action" app:title="@string/select_action" /> +