mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
add fps counter
This commit is contained in:
@@ -3,11 +3,16 @@ package com.izzy2lost.x1box
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Color
|
||||
import android.graphics.Typeface
|
||||
import android.hardware.input.InputManager
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.os.Process
|
||||
import android.util.TypedValue
|
||||
import android.view.Gravity
|
||||
import android.view.InputDevice
|
||||
import android.view.KeyEvent
|
||||
@@ -26,6 +31,7 @@ import android.widget.ListView
|
||||
import android.widget.RelativeLayout
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.widget.NestedScrollView
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
@@ -36,6 +42,7 @@ import java.io.File
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
import kotlin.math.max
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class MainActivity : SDLActivity(), InputManager.InputDeviceListener {
|
||||
companion object {
|
||||
@@ -66,6 +73,15 @@ class MainActivity : SDLActivity(), InputManager.InputDeviceListener {
|
||||
private var startupSnapshotSlot: Int? = null
|
||||
private var startupSnapshotLoadScheduled = false
|
||||
private lateinit var swipeUpGestureRecognizer: SwipeUpGestureRecognizer
|
||||
private var fpsTextView: TextView? = null
|
||||
private val fpsHandler = Handler(Looper.getMainLooper())
|
||||
private val fpsUpdateInterval = 1000L
|
||||
private val fpsRunnable = object : Runnable {
|
||||
override fun run() {
|
||||
fpsTextView?.text = "FPS: ${nativeGetFps()}"
|
||||
fpsHandler.postDelayed(this, fpsUpdateInterval)
|
||||
}
|
||||
}
|
||||
|
||||
override fun loadLibraries() {
|
||||
super.loadLibraries()
|
||||
@@ -134,6 +150,7 @@ class MainActivity : SDLActivity(), InputManager.InputDeviceListener {
|
||||
}
|
||||
initializeSwipeMenuGesture()
|
||||
setupOnScreenController()
|
||||
setupFpsOverlay()
|
||||
setupControllerDetection()
|
||||
hideSystemUI()
|
||||
}
|
||||
@@ -142,6 +159,7 @@ class MainActivity : SDLActivity(), InputManager.InputDeviceListener {
|
||||
super.onWindowFocusChanged(hasFocus)
|
||||
if (hasFocus) {
|
||||
hideSystemUI()
|
||||
updateFpsOverlayPosition()
|
||||
} else {
|
||||
// Release all on-screen inputs when the window loses focus (e.g. a system
|
||||
// gesture panel, notification shade, or dialog appears). Without this,
|
||||
@@ -250,6 +268,58 @@ class MainActivity : SDLActivity(), InputManager.InputDeviceListener {
|
||||
updateControllerVisibility()
|
||||
}
|
||||
|
||||
private fun setupFpsOverlay() {
|
||||
fpsTextView = TextView(this).apply {
|
||||
text = "FPS: --"
|
||||
setTextColor(ContextCompat.getColor(this@MainActivity, R.color.xemu_green))
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_SP, 11f)
|
||||
typeface = Typeface.MONOSPACE
|
||||
setShadowLayer(2f, 1f, 1f, Color.BLACK)
|
||||
setPadding(16, 8, 16, 8)
|
||||
setBackgroundColor(Color.argb(100, 0, 0, 0))
|
||||
maxLines = 1
|
||||
visibility = View.GONE
|
||||
}
|
||||
val params = RelativeLayout.LayoutParams(
|
||||
RelativeLayout.LayoutParams.WRAP_CONTENT,
|
||||
RelativeLayout.LayoutParams.WRAP_CONTENT
|
||||
).apply {
|
||||
addRule(RelativeLayout.ALIGN_PARENT_TOP)
|
||||
addRule(RelativeLayout.ALIGN_PARENT_START)
|
||||
}
|
||||
mLayout?.addView(fpsTextView, params)
|
||||
updateFpsOverlayPosition()
|
||||
}
|
||||
|
||||
private fun updateFpsOverlayPosition() {
|
||||
val hostLayout = mLayout ?: return
|
||||
val fpsView = fpsTextView ?: return
|
||||
|
||||
fpsView.post {
|
||||
val hostWidth = hostLayout.width.toFloat()
|
||||
if (hostWidth <= 0f) {
|
||||
return@post
|
||||
}
|
||||
|
||||
// Match the LT button geometry from OnScreenController so the overlay
|
||||
// stays tucked just to its right even as the screen size changes.
|
||||
val shoulderButtonRadius = hostWidth * 0.034f
|
||||
val shoulderEdgeMargin = shoulderButtonRadius + hostWidth * 0.02f
|
||||
val leftTriggerRightEdge = shoulderEdgeMargin + shoulderButtonRadius
|
||||
val triggerTopEdge = shoulderEdgeMargin - shoulderButtonRadius
|
||||
val gapPx = TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_DIP,
|
||||
10f,
|
||||
resources.displayMetrics
|
||||
).roundToInt()
|
||||
|
||||
val params = fpsView.layoutParams as? RelativeLayout.LayoutParams ?: return@post
|
||||
params.leftMargin = leftTriggerRightEdge.roundToInt() + gapPx
|
||||
params.topMargin = triggerTopEdge.roundToInt()
|
||||
fpsView.layoutParams = params
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (suspendedByLifecycle) {
|
||||
@@ -266,10 +336,20 @@ class MainActivity : SDLActivity(), InputManager.InputDeviceListener {
|
||||
registerVirtualController()
|
||||
}, 1000)
|
||||
|
||||
val showFps = getSharedPreferences("x1box_prefs", MODE_PRIVATE)
|
||||
.getBoolean("show_fps", false)
|
||||
fpsTextView?.visibility = if (showFps) View.VISIBLE else View.GONE
|
||||
fpsHandler.removeCallbacks(fpsRunnable)
|
||||
if (showFps) {
|
||||
fpsHandler.postDelayed(fpsRunnable, fpsUpdateInterval)
|
||||
}
|
||||
|
||||
updateFpsOverlayPosition()
|
||||
scheduleStartupSnapshotLoadIfRequested()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
fpsHandler.removeCallbacks(fpsRunnable)
|
||||
swipeUpGestureRecognizer.reset()
|
||||
onScreenController?.resetAllInputs()
|
||||
controllerBridge?.reset()
|
||||
@@ -396,6 +476,7 @@ class MainActivity : SDLActivity(), InputManager.InputDeviceListener {
|
||||
|
||||
override fun onDestroy() {
|
||||
DebugLog.i(TAG) { "onDestroy()" }
|
||||
fpsHandler.removeCallbacks(fpsRunnable)
|
||||
swipeUpGestureRecognizer.reset()
|
||||
resumeEmulationOnMenuDismiss = false
|
||||
inGameMenuDialog?.dismiss()
|
||||
@@ -501,6 +582,7 @@ class MainActivity : SDLActivity(), InputManager.InputDeviceListener {
|
||||
private external fun nativeSaveSnapshot(name: String): Boolean
|
||||
private external fun nativeLoadSnapshot(name: String): Boolean
|
||||
private external fun nativeRebootSystem()
|
||||
private external fun nativeGetFps(): Int
|
||||
private external fun nativePauseEmulation()
|
||||
private external fun nativeResumeEmulation()
|
||||
private external fun nativeExitEmulation()
|
||||
|
||||
@@ -315,6 +315,7 @@ class SettingsActivity : AppCompatActivity() {
|
||||
val switchDrawReorder = findViewById<MaterialSwitch>(R.id.switch_draw_reorder)
|
||||
val switchDrawMerge = findViewById<MaterialSwitch>(R.id.switch_draw_merge)
|
||||
val switchAsyncCompile = findViewById<MaterialSwitch>(R.id.switch_async_compile)
|
||||
val switchShowFps = findViewById<MaterialSwitch>(R.id.switch_show_fps)
|
||||
switchDebugLogs = findViewById(R.id.switch_debug_logs)
|
||||
val toggleAudioDriver = findViewById<MaterialButtonToggleGroup>(R.id.toggle_audio_driver)
|
||||
val btnSave = findViewById<MaterialButton>(R.id.btn_settings_save)
|
||||
@@ -433,6 +434,7 @@ class SettingsActivity : AppCompatActivity() {
|
||||
switchDrawReorder.isChecked = prefs.getBoolean("draw_reorder", true)
|
||||
switchDrawMerge.isChecked = prefs.getBoolean("draw_merge", true)
|
||||
switchAsyncCompile.isChecked = prefs.getBoolean("async_compile", false)
|
||||
switchShowFps.isChecked = prefs.getBoolean("show_fps", false)
|
||||
switchDebugLogs.isChecked =
|
||||
prefs.getBoolean(DebugLog.PREF_ENABLED, false)
|
||||
switchNetworkEnable.isChecked =
|
||||
@@ -528,6 +530,7 @@ class SettingsActivity : AppCompatActivity() {
|
||||
.putBoolean("draw_reorder", switchDrawReorder.isChecked)
|
||||
.putBoolean("draw_merge", switchDrawMerge.isChecked)
|
||||
.putBoolean("async_compile", switchAsyncCompile.isChecked)
|
||||
.putBoolean("show_fps", switchShowFps.isChecked)
|
||||
.putBoolean(DebugLog.PREF_ENABLED, enableDebugLogs)
|
||||
.putBoolean("setting_network_enable", switchNetworkEnable.isChecked)
|
||||
.putString("setting_audio_driver", selectedAudioDriver)
|
||||
|
||||
@@ -872,10 +872,18 @@
|
||||
android:textAppearance="@style/TextAppearance.Material3.LabelLarge"
|
||||
android:textColor="@color/xemu_green_light" />
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/switch_show_fps"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/settings_show_fps"
|
||||
android:textColor="@color/xemu_text_muted" />
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/switch_debug_logs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="@string/settings_debug_logs_enable"
|
||||
android:textColor="@color/xemu_text_muted" />
|
||||
|
||||
|
||||
@@ -290,6 +290,7 @@
|
||||
<string name="settings_dashboard_import_boot_alias_note">Boot compatibility: this import did not include C:\\xboxdash.xbe, so %1$s will also be copied as xboxdash.xbe.</string>
|
||||
<string name="settings_dashboard_import_boot_missing_note">Warning: no obvious dashboard XBE was found anywhere under C:. Boot Dashboard may still show the service screen.</string>
|
||||
<string name="settings_section_maintenance">Maintenance</string>
|
||||
<string name="settings_show_fps">Show FPS counter</string>
|
||||
<string name="settings_debug_logs_enable">Enable Debug Logs</string>
|
||||
<string name="settings_debug_logs_hint">Writes extra UI and emulator bootstrap logs only while enabled. Turn this on before reproducing a problem, then export the log.</string>
|
||||
<string name="settings_export_debug_log_action">Export Debug Log</string>
|
||||
|
||||
Reference in New Issue
Block a user