android: Improve performance stats overlay settings and functionality (#808)

* android: Improve performance stats overlay settings and functionality

* Add battery temp functions

* Readd frametime

* Corrected `perf_overlay_position` being placed in the wrong `default_ini.h` file

* Fixed the word "overlay" being repeatedly misspelled in function names

* `updateshowStatsOverlay` --> `updateShowStatsOverlay`

* Increased frequency of performance overlay updates

Changed from every 3 seconds to every 1 second

* Adjusted overlay margins to avoid text being lost behind rounded corner cutouts

* Fix performance overlay updates being stacked when changing orientation

* Changed out host RAM usage statistic for available host RAM

* Removed seemingly unused code

* "FT" --> "Frametime" in overlay

* Use non-breaking spaces to control how the overlay text breaks

Also used a vertical box drawing character instead of a pipe for the divider because it looks slightly nicer

* Renamed/adjusted remnants of the "Show System Memory Usage" setting

* Replaced Performance Stats Overlay icon with a stock clip art image from Android Studio

* Made performance overlay setting value names and strings less generic

* Rebranded Performance Stats Overlay as simply "Performance Overlay"

* Rewrote performance overlay settings description

* Improved naming consistency

* Rebranded "Show Overlay" toggle to "Show Controller Overlay"

This is to avoid confusion with the new performance overlay

* nitpick: Fixed order of imports in EmulationFragment.kt

* More string name consistency improvements

* Fixed compile failure due to a binding name not being updated

* Changed Performance Overlay setting headers

* EmulationFragment.kt: Formatting corrections

* Removed seemingly misplaced call to `updateShowPerformanceOverlay`

* `OVERLAY_POSITION` --> `PERFORMANCE_OVERLAY_POSITION`

---------

Co-authored-by: Kleidis <167202775+kleidis@users.noreply.github.com>
Co-authored-by: Zephyron <zephyron@citron-emu.orgq>
This commit is contained in:
OpenSauce 2025-05-08 19:26:30 +01:00 committed by GitHub
parent 16980b0ffc
commit 378d830a93
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 348 additions and 47 deletions

View File

@ -17,6 +17,13 @@ enum class BooleanSetting(
SWAP_SCREEN("swap_screen", Settings.SECTION_LAYOUT, false), SWAP_SCREEN("swap_screen", Settings.SECTION_LAYOUT, false),
INSTANT_DEBUG_LOG("instant_debug_log", Settings.SECTION_DEBUG, false), INSTANT_DEBUG_LOG("instant_debug_log", Settings.SECTION_DEBUG, false),
CUSTOM_LAYOUT("custom_layout",Settings.SECTION_LAYOUT,false), CUSTOM_LAYOUT("custom_layout",Settings.SECTION_LAYOUT,false),
OVERLAY_SHOW_FPS("overlay_show_fps", Settings.SECTION_LAYOUT, true),
OVERLAY_SHOW_FRAMETIME("overlay_show_frame_time", Settings.SECTION_LAYOUT, false),
OVERLAY_SHOW_SPEED("overlay_show_speed", Settings.SECTION_LAYOUT, false),
OVERLAY_SHOW_APP_RAM_USAGE("overlay_show_app_ram_usage", Settings.SECTION_LAYOUT, false),
OVERLAY_SHOW_AVAILABLE_RAM("overlay_show_available_ram", Settings.SECTION_LAYOUT, false),
OVERLAY_SHOW_BATTERY_TEMP("overlay_show_battery_temp", Settings.SECTION_LAYOUT, false),
OVERLAY_BACKGROUND("overlay_background", Settings.SECTION_LAYOUT, false),
DELAY_START_LLE_MODULES("delay_start_for_lle_modules", Settings.SECTION_DEBUG, true), DELAY_START_LLE_MODULES("delay_start_for_lle_modules", Settings.SECTION_DEBUG, true),
DETERMINISTIC_ASYNC_OPERATIONS("deterministic_async_operations", Settings.SECTION_DEBUG, false), DETERMINISTIC_ASYNC_OPERATIONS("deterministic_async_operations", Settings.SECTION_DEBUG, false),
REQUIRED_ONLINE_LLE_MODULES("enable_required_online_lle_modules", Settings.SECTION_SYSTEM, false); REQUIRED_ONLINE_LLE_MODULES("enable_required_online_lle_modules", Settings.SECTION_SYSTEM, false);

View File

@ -67,7 +67,8 @@ enum class IntSetting(
USE_ARTIC_BASE_CONTROLLER("use_artic_base_controller", Settings.SECTION_CONTROLS, 0), USE_ARTIC_BASE_CONTROLLER("use_artic_base_controller", Settings.SECTION_CONTROLS, 0),
ORIENTATION_OPTION("screen_orientation", Settings.SECTION_LAYOUT, 2), ORIENTATION_OPTION("screen_orientation", Settings.SECTION_LAYOUT, 2),
DISABLE_RIGHT_EYE_RENDER("disable_right_eye_render", Settings.SECTION_RENDERER, 0), DISABLE_RIGHT_EYE_RENDER("disable_right_eye_render", Settings.SECTION_RENDERER, 0),
TURBO_LIMIT("turbo_limit", Settings.SECTION_CORE, 200); TURBO_LIMIT("turbo_limit", Settings.SECTION_CORE, 200),
PERFORMANCE_OVERLAY_POSITION("performance_overlay_position", Settings.SECTION_LAYOUT, 0);
override var int: Int = defaultValue override var int: Int = defaultValue

View File

@ -111,6 +111,7 @@ class Settings {
const val SECTION_THEME = "Theme" const val SECTION_THEME = "Theme"
const val SECTION_CUSTOM_LANDSCAPE = "Custom Landscape Layout" const val SECTION_CUSTOM_LANDSCAPE = "Custom Landscape Layout"
const val SECTION_CUSTOM_PORTRAIT = "Custom Portrait Layout" const val SECTION_CUSTOM_PORTRAIT = "Custom Portrait Layout"
const val SECTION_PERFORMANCE_OVERLAY = "Performance Overlay"
const val KEY_BUTTON_A = "button_a" const val KEY_BUTTON_A = "button_a"
const val KEY_BUTTON_B = "button_b" const val KEY_BUTTON_B = "button_b"

View File

@ -47,6 +47,7 @@ import org.citra.citra_emu.utils.BirthdayMonth
import org.citra.citra_emu.utils.Log import org.citra.citra_emu.utils.Log
import org.citra.citra_emu.utils.SystemSaveGame import org.citra.citra_emu.utils.SystemSaveGame
import org.citra.citra_emu.utils.ThemeUtil import org.citra.citra_emu.utils.ThemeUtil
import org.citra.citra_emu.utils.EmulationMenuSettings
class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) { class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) {
private var menuTag: String? = null private var menuTag: String? = null
@ -102,6 +103,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
Settings.SECTION_THEME -> addThemeSettings(sl) Settings.SECTION_THEME -> addThemeSettings(sl)
Settings.SECTION_CUSTOM_LANDSCAPE -> addCustomLandscapeSettings(sl) Settings.SECTION_CUSTOM_LANDSCAPE -> addCustomLandscapeSettings(sl)
Settings.SECTION_CUSTOM_PORTRAIT -> addCustomPortraitSettings(sl) Settings.SECTION_CUSTOM_PORTRAIT -> addCustomPortraitSettings(sl)
Settings.SECTION_PERFORMANCE_OVERLAY -> addPerformanceOverlaySettings(sl)
else -> { else -> {
fragmentView.showToastMessage("Unimplemented menu", false) fragmentView.showToastMessage("Unimplemented menu", false)
return return
@ -1116,6 +1118,14 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
FloatSetting.LARGE_SCREEN_PROPORTION.defaultValue FloatSetting.LARGE_SCREEN_PROPORTION.defaultValue
) )
) )
add(
SubmenuSetting(
R.string.performance_overlay_options,
R.string.performance_overlay_options_description,
R.drawable.ic_stats,
Settings.SECTION_PERFORMANCE_OVERLAY
)
)
add( add(
SubmenuSetting( SubmenuSetting(
R.string.emulation_landscape_custom_layout, R.string.emulation_landscape_custom_layout,
@ -1135,6 +1145,116 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
} }
} }
private fun addPerformanceOverlaySettings(sl: ArrayList<SettingsItem>) {
settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.performance_overlay_options))
sl.apply {
add(HeaderSetting(R.string.visibility))
add(
SwitchSetting(
object : AbstractBooleanSetting {
override val key = "EmulationMenuSettings_showPerfPerformanceOverlay"
override val section = Settings.SECTION_LAYOUT
override val defaultValue = false
override var boolean: Boolean
get() = EmulationMenuSettings.showPerformanceOverlay
set(value) { EmulationMenuSettings.showPerformanceOverlay = value }
override val isRuntimeEditable = true
override val valueAsString: String get() = boolean.toString()
},
R.string.performance_overlay_enable,
0,
"EmulationMenuSettings_showPerfPerformanceOverlay",
false
)
)
add(
SwitchSetting(
BooleanSetting.OVERLAY_BACKGROUND,
R.string.overlay_background,
R.string.overlay_background_description,
"overlay_background",
false
)
)
add(
SingleChoiceSetting(
IntSetting.PERFORMANCE_OVERLAY_POSITION,
R.string.overlay_position,
R.string.overlay_position_description,
R.array.statsPosition,
R.array.statsPositionValues,
)
)
add(HeaderSetting(R.string.information))
add(
SwitchSetting(
BooleanSetting.OVERLAY_SHOW_FPS,
R.string.overlay_show_fps,
R.string.overlay_show_fps_description,
"overlay_show_fps",
true
)
)
add(
SwitchSetting(
BooleanSetting.OVERLAY_SHOW_FRAMETIME,
R.string.overlay_show_frametime,
R.string.overlay_show_frametime_description,
"overlay_show_frame_time",
true
)
)
add(
SwitchSetting(
BooleanSetting.OVERLAY_SHOW_SPEED,
R.string.overlay_show_speed,
R.string.overlay_show_speed_description,
"overlay_show_speed",
false
)
)
add(
SwitchSetting(
BooleanSetting.OVERLAY_SHOW_APP_RAM_USAGE,
R.string.overlay_show_app_ram_usage,
R.string.overlay_show_app_ram_usage_description,
"overlay_show_app_ram_usage",
false
)
)
add(
SwitchSetting(
BooleanSetting.OVERLAY_SHOW_AVAILABLE_RAM,
R.string.overlay_show_available_ram,
R.string.overlay_show_available_ram_description,
"overlay_show_available_ram",
false
)
)
add(
SwitchSetting(
BooleanSetting.OVERLAY_SHOW_BATTERY_TEMP,
R.string.overlay_show_battery_temp,
R.string.overlay_show_battery_temp_description,
"overlay_show_battery_temp",
false
)
)
}
}
private fun addCustomLandscapeSettings(sl: ArrayList<SettingsItem>) { private fun addCustomLandscapeSettings(sl: ArrayList<SettingsItem>) {
settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.emulation_landscape_custom_layout)) settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.emulation_landscape_custom_layout))
sl.apply { sl.apply {

View File

@ -5,10 +5,14 @@
package org.citra.citra_emu.fragments package org.citra.citra_emu.fragments
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.ActivityManager
import android.content.Context import android.content.Context
import android.content.DialogInterface import android.content.DialogInterface
import android.content.Intent
import android.content.IntentFilter
import android.content.SharedPreferences import android.content.SharedPreferences
import android.net.Uri import android.net.Uri
import android.os.BatteryManager
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
@ -16,6 +20,7 @@ import android.os.SystemClock
import android.text.Editable import android.text.Editable
import android.text.TextWatcher import android.text.TextWatcher
import android.view.Choreographer import android.view.Choreographer
import android.view.Gravity
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.MotionEvent import android.view.MotionEvent
import android.view.Surface import android.view.Surface
@ -27,6 +32,7 @@ import android.widget.PopupMenu
import android.widget.TextView import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.Insets import androidx.core.graphics.Insets
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
@ -44,6 +50,7 @@ import androidx.navigation.fragment.navArgs
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.slider.Slider import com.google.android.material.slider.Slider
import java.io.File
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.citra.citra_emu.CitraApplication import org.citra.citra_emu.CitraApplication
@ -57,6 +64,7 @@ import org.citra.citra_emu.databinding.FragmentEmulationBinding
import org.citra.citra_emu.display.PortraitScreenLayout import org.citra.citra_emu.display.PortraitScreenLayout
import org.citra.citra_emu.display.ScreenAdjustmentUtil import org.citra.citra_emu.display.ScreenAdjustmentUtil
import org.citra.citra_emu.display.ScreenLayout import org.citra.citra_emu.display.ScreenLayout
import org.citra.citra_emu.features.settings.model.BooleanSetting
import org.citra.citra_emu.features.settings.model.IntSetting import org.citra.citra_emu.features.settings.model.IntSetting
import org.citra.citra_emu.features.settings.model.SettingsViewModel import org.citra.citra_emu.features.settings.model.SettingsViewModel
import org.citra.citra_emu.features.settings.ui.SettingsActivity import org.citra.citra_emu.features.settings.ui.SettingsActivity
@ -175,8 +183,11 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
binding.surfaceInputOverlay.setIsInEditMode(false) binding.surfaceInputOverlay.setIsInEditMode(false)
} }
// Show/hide the "Show FPS" overlay // Show/hide the "Stats" overlay
updateShowFpsOverlay() updateShowPerformanceOverlay()
val position = IntSetting.PERFORMANCE_OVERLAY_POSITION.int
updateStatsPosition(position)
binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED) binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
binding.drawerLayout.addDrawerListener(object : DrawerListener { binding.drawerLayout.addDrawerListener(object : DrawerListener {
@ -455,6 +466,11 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
Choreographer.getInstance().postFrameCallback(this) Choreographer.getInstance().postFrameCallback(this)
if (NativeLibrary.isRunning()) { if (NativeLibrary.isRunning()) {
NativeLibrary.unPauseEmulation() NativeLibrary.unPauseEmulation()
// If the overlay is enabled, we need to update the position if changed
val position = IntSetting.PERFORMANCE_OVERLAY_POSITION.int
updateStatsPosition(position)
binding.inGameMenu.menu.findItem(R.id.menu_emulation_pause)?.let { menuItem -> binding.inGameMenu.menu.findItem(R.id.menu_emulation_pause)?.let { menuItem ->
menuItem.title = resources.getString(R.string.pause_emulation) menuItem.title = resources.getString(R.string.pause_emulation)
menuItem.icon = ResourcesCompat.getDrawable( menuItem.icon = ResourcesCompat.getDrawable(
@ -640,7 +656,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
popupMenu.menu.apply { popupMenu.menu.apply {
findItem(R.id.menu_show_overlay).isChecked = EmulationMenuSettings.showOverlay findItem(R.id.menu_show_overlay).isChecked = EmulationMenuSettings.showOverlay
findItem(R.id.menu_show_fps).isChecked = EmulationMenuSettings.showFps findItem(R.id.menu_performance_overlay_show).isChecked =
EmulationMenuSettings.showPerformanceOverlay
findItem(R.id.menu_haptic_feedback).isChecked = EmulationMenuSettings.hapticFeedback findItem(R.id.menu_haptic_feedback).isChecked = EmulationMenuSettings.hapticFeedback
findItem(R.id.menu_emulation_joystick_rel_center).isChecked = findItem(R.id.menu_emulation_joystick_rel_center).isChecked =
EmulationMenuSettings.joystickRelCenter EmulationMenuSettings.joystickRelCenter
@ -656,15 +673,14 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
true true
} }
R.id.menu_show_fps -> { R.id.menu_performance_overlay_show -> {
EmulationMenuSettings.showFps = !EmulationMenuSettings.showFps EmulationMenuSettings.showPerformanceOverlay = !EmulationMenuSettings.showPerformanceOverlay
updateShowFpsOverlay() updateShowPerformanceOverlay()
true true
} }
R.id.menu_haptic_feedback -> { R.id.menu_haptic_feedback -> {
EmulationMenuSettings.hapticFeedback = !EmulationMenuSettings.hapticFeedback EmulationMenuSettings.hapticFeedback = !EmulationMenuSettings.hapticFeedback
updateShowFpsOverlay()
true true
} }
@ -1149,34 +1165,140 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
binding.surfaceInputOverlay.resetButtonPlacement() binding.surfaceInputOverlay.resetButtonPlacement()
} }
fun updateShowFpsOverlay() { fun updateShowPerformanceOverlay() {
if (EmulationMenuSettings.showFps) { if (perfStatsUpdater != null) {
perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
}
if (EmulationMenuSettings.showPerformanceOverlay) {
val SYSTEM_FPS = 0 val SYSTEM_FPS = 0
val FPS = 1 val FPS = 1
val FRAMETIME = 2 val FRAMETIME = 2
val SPEED = 3 val SPEED = 3
perfStatsUpdater = Runnable { perfStatsUpdater = Runnable {
val sb = StringBuilder()
val perfStats = NativeLibrary.getPerfStats() val perfStats = NativeLibrary.getPerfStats()
val dividerString = "\u00A0\u2502 "
if (perfStats[FPS] > 0) { if (perfStats[FPS] > 0) {
binding.showFpsText.text = String.format( if (BooleanSetting.OVERLAY_SHOW_FPS.boolean) {
"FPS: %d Speed: %d%% FT: %.2fms", sb.append(String.format("FPS:\u00A0%d", (perfStats[FPS] + 0.5).toInt()))
(perfStats[FPS] + 0.5).toInt(), }
(perfStats[SPEED] * 100.0 + 0.5).toInt(),
if (BooleanSetting.OVERLAY_SHOW_FRAMETIME.boolean) {
if (sb.isNotEmpty()) sb.append(dividerString)
sb.append(
String.format(
"Frametime:\u00A0%.1fms",
(perfStats[FRAMETIME] * 1000.0f).toFloat() (perfStats[FRAMETIME] * 1000.0f).toFloat()
) )
)
} }
perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 3000)
if (BooleanSetting.OVERLAY_SHOW_SPEED.boolean) {
if (sb.isNotEmpty()) sb.append(dividerString)
sb.append(
String.format(
"Speed:\u00A0%d%%",
(perfStats[SPEED] * 100.0 + 0.5).toInt()
)
)
}
if (BooleanSetting.OVERLAY_SHOW_APP_RAM_USAGE.boolean) {
if (sb.isNotEmpty()) sb.append(dividerString)
val appRamUsage =
File("/proc/self/statm").readLines()[0].split(' ')[1].toLong() * 4096 / 1000000
sb.append("Process\u00A0RAM:\u00A0$appRamUsage\u00A0MB")
}
if (BooleanSetting.OVERLAY_SHOW_AVAILABLE_RAM.boolean) {
if (sb.isNotEmpty()) sb.append(dividerString)
context?.let { ctx ->
val activityManager =
ctx.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val memInfo = ActivityManager.MemoryInfo()
activityManager.getMemoryInfo(memInfo)
val megabyteBytes = 1048576L
val availableRam = memInfo.availMem / megabyteBytes
sb.append("Available\u00A0RAM:\u00A0$availableRam\u00A0MB")
}
}
if (BooleanSetting.OVERLAY_SHOW_BATTERY_TEMP.boolean) {
if (sb.isNotEmpty()) sb.append(dividerString)
val batteryTemp = getBatteryTemperature()
val tempF = celsiusToFahrenheit(batteryTemp)
sb.append(String.format("%.1f°C/%.1f°F", batteryTemp, tempF))
}
if (BooleanSetting.OVERLAY_BACKGROUND.boolean) {
binding.performanceOverlayShowText.setBackgroundResource(R.color.citra_transparent_black)
} else {
binding.performanceOverlayShowText.setBackgroundResource(0)
}
binding.performanceOverlayShowText.text = sb.toString()
}
perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 1000)
} }
perfStatsUpdateHandler.post(perfStatsUpdater!!) perfStatsUpdateHandler.post(perfStatsUpdater!!)
binding.showFpsText.visibility = View.VISIBLE binding.performanceOverlayShowText.visibility = View.VISIBLE
} else { } else {
if (perfStatsUpdater != null) { binding.performanceOverlayShowText.visibility = View.GONE
perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
}
binding.showFpsText.visibility = View.GONE
} }
} }
private fun updateStatsPosition(position: Int) {
val params = binding.performanceOverlayShowText.layoutParams as CoordinatorLayout.LayoutParams
val padding = (20 * resources.displayMetrics.density).toInt() // 20dp
params.setMargins(padding, 0, padding, 0)
when (position) {
0 -> {
params.gravity = (Gravity.TOP or Gravity.START)
}
1 -> {
params.gravity = (Gravity.TOP or Gravity.CENTER_HORIZONTAL)
}
2 -> {
params.gravity = (Gravity.TOP or Gravity.END)
}
3 -> {
params.gravity = (Gravity.BOTTOM or Gravity.START)
}
4 -> {
params.gravity = (Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL)
}
5 -> {
params.gravity = (Gravity.BOTTOM or Gravity.END)
}
}
binding.performanceOverlayShowText.layoutParams = params
}
private fun getBatteryTemperature(): Float {
try {
val batteryIntent = requireContext().registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
// Temperature in tenths of a degree Celsius
val temperature = batteryIntent?.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 0) ?: 0
// Convert to degrees Celsius
return temperature / 10.0f
} catch (e: Exception) {
return 0.0f
}
}
private fun celsiusToFahrenheit(celsius: Float): Float {
return (celsius * 9 / 5) + 32
}
override fun surfaceCreated(holder: SurfaceHolder) { override fun surfaceCreated(holder: SurfaceHolder) {
// We purposely don't do anything here. // We purposely don't do anything here.
// All work is done in surfaceChanged, which we are guaranteed to get even for surface creation. // All work is done in surfaceChanged, which we are guaranteed to get even for surface creation.
@ -1211,23 +1333,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
v.setPadding(left, cutInsets.top, right, 0) v.setPadding(left, cutInsets.top, right, 0)
// Ensure FPS text doesn't get cut off by rounded display corners
val sidePadding = resources.getDimensionPixelSize(R.dimen.spacing_large)
if (cutInsets.left == 0) {
binding.showFpsText.setPadding(
sidePadding,
cutInsets.top,
cutInsets.right,
cutInsets.bottom
)
} else {
binding.showFpsText.setPadding(
cutInsets.left,
cutInsets.top,
cutInsets.right,
cutInsets.bottom
)
}
windowInsets windowInsets
} }
} }

View File

@ -27,11 +27,11 @@ object EmulationMenuSettings {
.apply() .apply()
} }
var showFps: Boolean var showPerformanceOverlay: Boolean
get() = preferences.getBoolean("EmulationMenuSettings_ShowFps", false) get() = preferences.getBoolean("EmulationMenuSettings_showPerformanceOverlay", false)
set(value) { set(value) {
preferences.edit() preferences.edit()
.putBoolean("EmulationMenuSettings_ShowFps", value) .putBoolean("EmulationMenuSettings_showPerformanceOverlay", value)
.apply() .apply()
} }
var hapticFeedback: Boolean var hapticFeedback: Boolean

View File

@ -206,6 +206,15 @@ disable_right_eye_render =
# 5: Custom Layout # 5: Custom Layout
layout_option = layout_option =
# Position of the performance overlay
# 0: Top Left
# 1: Center Top
# 2: Top Right
# 3: Bottom Left
# 4: Center Bottom
# 5: Bottom Right
performance_overlay_position =
# Screen Gap - adds a gap between screens in all two-screen modes # Screen Gap - adds a gap between screens in all two-screen modes
# Measured in pixels relative to the 240px default height of the screens # Measured in pixels relative to the 240px default height of the screens
# Scales with the larger screen (so 24 is 10% of the larger screen height) # Scales with the larger screen (so 24 is 10% of the larger screen height)

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M19.88,18.47c0.44,-0.7 0.7,-1.51 0.7,-2.39c0,-2.49 -2.01,-4.5 -4.5,-4.5s-4.5,2.01 -4.5,4.5s2.01,4.5 4.49,4.5c0.88,0 1.7,-0.26 2.39,-0.7L21.58,23L23,21.58L19.88,18.47zM16.08,18.58c-1.38,0 -2.5,-1.12 -2.5,-2.5c0,-1.38 1.12,-2.5 2.5,-2.5s2.5,1.12 2.5,2.5C18.58,17.46 17.46,18.58 16.08,18.58zM15.72,10.08c-0.74,0.02 -1.45,0.18 -2.1,0.45l-0.55,-0.83l-3.8,6.18l-3.01,-3.52l-3.63,5.81L1,17l5,-8l3,3.5L13,6C13,6 15.72,10.08 15.72,10.08zM18.31,10.58c-0.64,-0.28 -1.33,-0.45 -2.05,-0.49c0,0 5.12,-8.09 5.12,-8.09L23,3.18L18.31,10.58z"/>
</vector>

View File

@ -117,7 +117,7 @@
</com.google.android.material.card.MaterialCardView> </com.google.android.material.card.MaterialCardView>
<TextView <TextView
android:id="@+id/show_fps_text" android:id="@+id/performance_overlay_show_text"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="left" android:layout_gravity="left"

View File

@ -3,12 +3,12 @@
<item <item
android:id="@+id/menu_show_overlay" android:id="@+id/menu_show_overlay"
android:title="@string/emulation_show_overlay" android:title="@string/emulation_show_controller_overlay"
android:checkable="true" /> android:checkable="true" />
<item <item
android:id="@+id/menu_show_fps" android:id="@+id/menu_performance_overlay_show"
android:title="@string/emulation_show_fps" android:title="@string/performance_overlay_show"
android:checkable="true" /> android:checkable="true" />
<item <item

View File

@ -227,6 +227,7 @@
<color name="citra_surfaceTint_gray">#B7B7B7</color> <color name="citra_surfaceTint_gray">#B7B7B7</color>
<!-- Common Colors Across All Themes --> <!-- Common Colors Across All Themes -->
<color name="citra_transparent_black">#80000000</color>
<color name="citra_outlineVariant">#C6C5D0</color> <color name="citra_outlineVariant">#C6C5D0</color>
<color name="citra_error">#FFB4AB</color> <color name="citra_error">#FFB4AB</color>
<color name="citra_errorContainer">#93000A</color> <color name="citra_errorContainer">#93000A</color>

View File

@ -115,6 +115,23 @@
<item>11</item> <item>11</item>
</integer-array> </integer-array>
<string-array name="statsPosition">
<item>@string/overlay_position_top_left</item>
<item>@string/overlay_position_center_top</item>
<item>@string/overlay_position_top_right</item>
<item>@string/overlay_position_bottom_left</item>
<item>@string/overlay_position_center_bottom</item>
<item>@string/overlay_position_bottom_right</item>
</string-array>
<integer-array name="statsPositionValues">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
<item>5</item>
</integer-array>
<string-array name="n3dsButtons"> <string-array name="n3dsButtons">
<item>@string/button_a</item> <item>@string/button_a</item>
<item>@string/button_b</item> <item>@string/button_b</item>

View File

@ -222,6 +222,7 @@
<color name="citra_surfaceTint_gray">#9E9E9E</color> <color name="citra_surfaceTint_gray">#9E9E9E</color>
<!-- Common Colors Across All Themes --> <!-- Common Colors Across All Themes -->
<color name="citra_transparent_black">#80000000</color>
<color name="citra_outlineVariant">#C6C5D0</color> <color name="citra_outlineVariant">#C6C5D0</color>
<color name="citra_error">#BA1A1A</color> <color name="citra_error">#BA1A1A</color>
<color name="citra_errorContainer">#FFDAD6</color> <color name="citra_errorContainer">#FFDAD6</color>

View File

@ -362,6 +362,8 @@
<string name="cancelling">Cancelling…</string> <string name="cancelling">Cancelling…</string>
<string name="important">Important</string> <string name="important">Important</string>
<string name="dont_show_again">Don\'t show again</string> <string name="dont_show_again">Don\'t show again</string>
<string name="visibility">Visibility</string>
<string name="information">Information</string>
<!-- Add Directory Screen--> <!-- Add Directory Screen-->
<string name="select_game_folder">Select Game Folder</string> <string name="select_game_folder">Select Game Folder</string>
@ -445,7 +447,7 @@
<string name="emulation_cycle_landscape_layouts">Cycle Layouts</string> <string name="emulation_cycle_landscape_layouts">Cycle Layouts</string>
<string name="emulation_swap_screens">Swap Screens</string> <string name="emulation_swap_screens">Swap Screens</string>
<string name="emulation_touch_overlay_reset">Reset Overlay</string> <string name="emulation_touch_overlay_reset">Reset Overlay</string>
<string name="emulation_show_overlay">Show Overlay</string> <string name="emulation_show_controller_overlay">Show Controller Overlay</string>
<string name="emulation_close_game">Close Game</string> <string name="emulation_close_game">Close Game</string>
<string name="emulation_toggle_pause">Toggle Pause</string> <string name="emulation_toggle_pause">Toggle Pause</string>
<string name="miscellaneous">Miscellaneous</string> <string name="miscellaneous">Miscellaneous</string>
@ -534,6 +536,34 @@
<string name="game_context_uninstall_dlc">Uninstall DLC</string> <string name="game_context_uninstall_dlc">Uninstall DLC</string>
<string name="game_context_uninstall_updates">Uninstall Updates</string> <string name="game_context_uninstall_updates">Uninstall Updates</string>
<!-- Performance Overlay settings -->
<string name="performance_overlay_show">Show Performance Overlay</string>
<string name="performance_overlay_options">Performance Overlay</string>
<string name="performance_overlay_enable">Enable Performance Overlay</string>
<string name="performance_overlay_options_description">Configure whether the performance overlay is shown and what information is displayed.</string>
<string name="overlay_show_fps">Show FPS</string>
<string name="overlay_show_fps_description">Display current frames per second</string>
<string name="overlay_show_frametime">Show Frametime</string>
<string name="overlay_show_frametime_description">Display current frametime</string>
<string name="overlay_show_speed">Show Speed</string>
<string name="overlay_show_speed_description">Display current emulation speed percentage</string>
<string name="overlay_show_app_ram_usage">Show App Memory Usage</string>
<string name="overlay_show_app_ram_usage_description">Display the amount of RAM getting used by the emulator</string>
<string name="overlay_show_available_ram">Show Available Memory</string>
<string name="overlay_show_available_ram_description">Display the amount of RAM which is available</string>
<string name="overlay_show_battery_temp">Show Battery Temperature</string>
<string name="overlay_show_battery_temp_description">Display current Battery temperature in Celsius and Fahrenheit</string>
<string name="overlay_position">Overlay Position</string>
<string name="overlay_position_description">Choose where the performance overlay is displayed on the screen</string>
<string name="overlay_position_top_left">Top Left</string>
<string name="overlay_position_top_right">Top Right</string>
<string name="overlay_position_bottom_left">Bottom Left</string>
<string name="overlay_position_bottom_right">Bottom Right</string>
<string name="overlay_position_center_top">Center Top</string>
<string name="overlay_position_center_bottom">Center Bottom</string>
<string name="overlay_background">Overlay Background</string>
<string name="overlay_background_description">Adds a background behind the overlay for easier reading</string>
<!-- Cheats --> <!-- Cheats -->
<string name="cheats">Cheats</string> <string name="cheats">Cheats</string>
<string name="cheats_add">Add Cheat</string> <string name="cheats_add">Add Cheat</string>