From fe51be43c195a716d1d7eab5da9ae99017566d8d Mon Sep 17 00:00:00 2001 From: Briar <205427297+icy-briar@users.noreply.github.com> Date: Sun, 13 Apr 2025 05:36:30 +0200 Subject: [PATCH] android: Enhance FPS Overlay with more customizable options - Now the fps follows theme color set in settings - Added the ability to toggle stats on and off depending on user preference - Now you are able to change the fps position and add a background behind it for easier reding New added stats for the overlay are FPS FRAMETIME, SPEED, APP_RAM_USAGE, SYSTEM_RAM_USAGE, BATTERY_TEMPERATURE, --- .../features/settings/model/BooleanSetting.kt | 10 +- .../features/settings/model/IntSetting.kt | 1 + .../features/settings/model/Settings.kt | 2 + .../settings/model/view/SettingsItem.kt | 65 ++++ .../settings/ui/SettingsFragmentPresenter.kt | 27 ++ .../yuzu_emu/fragments/EmulationFragment.kt | 308 +++++++++++------- .../app/src/main/jni/android_settings.h | 20 +- .../app/src/main/res/drawable/ic_frames.xml | 32 ++ .../main/res/layout/fragment_emulation.xml | 6 +- .../src/main/res/layout/header_in_game.xml | 66 +++- .../main/res/menu/menu_overlay_options.xml | 9 +- .../src/main/res/values-night/yuzu_colors.xml | 1 + .../app/src/main/res/values/arrays.xml | 17 + .../app/src/main/res/values/strings.xml | 31 ++ .../app/src/main/res/values/yuzu_colors.xml | 1 + 15 files changed, 455 insertions(+), 141 deletions(-) create mode 100644 src/android/app/src/main/res/drawable/ic_frames.xml diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt index 6644784729..e3d76d4931 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt @@ -26,7 +26,15 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting { SHOW_PERFORMANCE_OVERLAY("show_performance_overlay"), SHOW_INPUT_OVERLAY("show_input_overlay"), TOUCHSCREEN("touchscreen"), - SHOW_THERMAL_OVERLAY("show_thermal_overlay"); + SHOW_THERMAL_OVERLAY("show_thermal_overlay"), + SHOW_FPS("show_fps"), + SHOW_FRAMETIME("show_frame_time"), + SHOW_SPEED("show_speed"), + SHOW_APP_RAM_USAGE("show_app_ram_usage"), + SHOW_SYSTEM_RAM_USAGE("show_system_ram_usage"), + SHOW_BAT_TEMPERATURE("show_bat_temperature"), + OVERLAY_BACKGROUND("overlay_background"),; + override fun getBoolean(needsGlobal: Boolean): Boolean = NativeConfig.getBoolean(key, needsGlobal) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt index 0165cb2d1d..b9658d0f67 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt @@ -26,6 +26,7 @@ enum class IntSetting(override val key: String) : AbstractIntSetting { OVERLAY_OPACITY("control_opacity"), LOCK_DRAWER("lock_drawer"), VERTICAL_ALIGNMENT("vertical_alignment"), + PERF_OVERLAY_POSITION("perf_overlay_position"), FSR_SHARPENING_SLIDER("fsr_sharpening_slider"); override fun getInt(needsGlobal: Boolean): Int = NativeConfig.getInt(key, needsGlobal) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt index e189c21560..a4d745621a 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt @@ -12,6 +12,7 @@ object Settings { SECTION_ROOT(R.string.advanced_settings), SECTION_SYSTEM(R.string.preferences_system), SECTION_RENDERER(R.string.preferences_graphics), + SECTION_PERFORMANCE_STATS(R.string.show_stats_overlay), SECTION_AUDIO(R.string.preferences_audio), SECTION_INPUT(R.string.preferences_controls), SECTION_INPUT_PLAYER_ONE, @@ -32,6 +33,7 @@ object Settings { const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch" const val PREF_SHOULD_SHOW_PRE_ALPHA_WARNING = "ShouldShowPreAlphaWarning" const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown" + const val SECTION_STATS_OVERLAY = "Stats Overlay" // Deprecated input overlay preference keys const val PREF_CONTROL_SCALE = "controlScale" diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt index 5fdf983185..8b54ec23e7 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt @@ -220,6 +220,71 @@ abstract class SettingsItem( valuesId = R.array.rendererResolutionValues ) ) + put( + SwitchSetting( + BooleanSetting.SHOW_PERFORMANCE_OVERLAY, + R.string.enable_stats_overlay_, + descriptionId = R.string.stats_overlay_options_description + ) + ) + put( + SwitchSetting( + BooleanSetting.OVERLAY_BACKGROUND, + R.string.overlay_background, + descriptionId = R.string.overlay_background_description + ) + ) + put( + SingleChoiceSetting( + IntSetting.PERF_OVERLAY_POSITION, + titleId = R.string.overlay_position, + descriptionId = R.string.overlay_position_description, + choicesId = R.array.statsPosition, + valuesId = R.array.staticThemeValues + ) + ) + put( + SwitchSetting( + BooleanSetting.SHOW_FPS, + R.string.show_fps, + descriptionId = R.string.show_fps_description + ) + ) + put( + SwitchSetting( + BooleanSetting.SHOW_FRAMETIME, + R.string.show_frametime, + descriptionId = R.string.show_frametime_description + ) + ) + put( + SwitchSetting( + BooleanSetting.SHOW_SPEED, + R.string.show_speed, + descriptionId = R.string.show_speed_description + ) + ) + put( + SwitchSetting( + BooleanSetting.SHOW_APP_RAM_USAGE, + R.string.show_app_ram_usage, + descriptionId = R.string.show_app_ram_usage_description + ) + ) + put( + SwitchSetting( + BooleanSetting.SHOW_SYSTEM_RAM_USAGE, + R.string.show_system_ram_usage, + descriptionId = R.string.show_system_ram_usage_description + ) + ) + put( + SwitchSetting( + BooleanSetting.SHOW_BAT_TEMPERATURE, + R.string.show_bat_temperature, + descriptionId = R.string.show_bat_temperature_description + ) + ) put( SingleChoiceSetting( IntSetting.RENDERER_VSYNC, diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt index 7fa22b272f..7031d41654 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt @@ -88,6 +88,7 @@ class SettingsFragmentPresenter( MenuTag.SECTION_ROOT -> addConfigSettings(sl) MenuTag.SECTION_SYSTEM -> addSystemSettings(sl) MenuTag.SECTION_RENDERER -> addGraphicsSettings(sl) + MenuTag.SECTION_PERFORMANCE_STATS -> addPerfomanceOverlaySettings(sl) MenuTag.SECTION_AUDIO -> addAudioSettings(sl) MenuTag.SECTION_INPUT -> addInputSettings(sl) MenuTag.SECTION_INPUT_PLAYER_ONE -> addInputPlayer(sl, 0) @@ -127,6 +128,15 @@ class SettingsFragmentPresenter( menuKey = MenuTag.SECTION_RENDERER ) ) + if (!NativeConfig.isPerGameConfigLoaded()) + add( + SubmenuSetting( + titleId = R.string.stats_overlay_options, + descriptionId = R.string.stats_overlay_options_description, + iconId = R.drawable.ic_frames, + menuKey = MenuTag.SECTION_PERFORMANCE_STATS + ) + ) add( SubmenuSetting( titleId = R.string.preferences_audio, @@ -187,6 +197,23 @@ class SettingsFragmentPresenter( } } + private fun addPerfomanceOverlaySettings(sl: ArrayList) { + sl.apply { + add(HeaderSetting(R.string.stats_overlay_customization)) + add(BooleanSetting.SHOW_PERFORMANCE_OVERLAY.key) + add(BooleanSetting.OVERLAY_BACKGROUND.key) + add(IntSetting.PERF_OVERLAY_POSITION.key) + add(HeaderSetting(R.string.stats_overlay_items)) + add(BooleanSetting.SHOW_FPS.key) + add(BooleanSetting.SHOW_FRAMETIME.key) + add(BooleanSetting.SHOW_SPEED.key) + add(BooleanSetting.SHOW_APP_RAM_USAGE.key) + add(BooleanSetting.SHOW_SYSTEM_RAM_USAGE.key) + add(BooleanSetting.SHOW_BAT_TEMPERATURE.key) + } + + } + private fun addAudioSettings(sl: ArrayList) { sl.apply { add(IntSetting.AUDIO_OUTPUT_ENGINE.key) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt index 2c99f6a2ac..43997db889 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt @@ -4,18 +4,29 @@ package org.yuzu.yuzu_emu.fragments import android.annotation.SuppressLint +import android.app.ActivityManager import android.app.AlertDialog import android.content.Context import android.content.DialogInterface +import android.content.Intent +import android.content.IntentFilter import android.content.pm.ActivityInfo import android.content.res.Configuration +import android.graphics.Color import android.net.Uri +import android.os.BatteryManager import android.os.Bundle import android.os.Handler import android.os.Looper import android.os.SystemClock import android.util.Rational -import android.view.* +import android.view.Gravity +import android.view.LayoutInflater +import android.view.MotionEvent +import android.view.Surface +import android.view.SurfaceHolder +import android.view.View +import android.view.ViewGroup import android.widget.FrameLayout import android.widget.TextView import android.widget.Toast @@ -26,7 +37,6 @@ import androidx.core.graphics.Insets import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.updateLayoutParams -import androidx.core.view.updatePadding import androidx.drawerlayout.widget.DrawerLayout import androidx.drawerlayout.widget.DrawerLayout.DrawerListener import androidx.fragment.app.Fragment @@ -36,6 +46,7 @@ import androidx.navigation.fragment.navArgs import androidx.window.layout.FoldingFeature import androidx.window.layout.WindowInfoTracker import androidx.window.layout.WindowLayoutInfo +import com.google.android.material.color.MaterialColors import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.slider.Slider import org.yuzu.yuzu_emu.HomeNavigationDirections @@ -51,28 +62,28 @@ import org.yuzu.yuzu_emu.features.settings.model.Settings.EmulationOrientation import org.yuzu.yuzu_emu.features.settings.model.Settings.EmulationVerticalAlignment import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile import org.yuzu.yuzu_emu.model.DriverViewModel -import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.model.EmulationViewModel +import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.overlay.model.OverlayControl import org.yuzu.yuzu_emu.overlay.model.OverlayLayout -import org.yuzu.yuzu_emu.utils.* +import org.yuzu.yuzu_emu.utils.DirectoryInitialization +import org.yuzu.yuzu_emu.utils.FileUtil +import org.yuzu.yuzu_emu.utils.GameHelper +import org.yuzu.yuzu_emu.utils.GameIconUtils +import org.yuzu.yuzu_emu.utils.Log +import org.yuzu.yuzu_emu.utils.NativeConfig +import org.yuzu.yuzu_emu.utils.ViewUtils import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible -import java.lang.NullPointerException -import android.content.BroadcastReceiver -import android.content.Intent -import android.content.IntentFilter -import android.os.BatteryManager -import android.util.TypedValue -import android.app.ActivityManager -import android.graphics.Color -import android.os.Debug +import org.yuzu.yuzu_emu.utils.collect +import java.io.File class EmulationFragment : Fragment(), SurfaceHolder.Callback { private lateinit var emulationState: EmulationState private var emulationActivity: EmulationActivity? = null private var perfStatsUpdater: (() -> Unit)? = null - private var thermalStatsUpdater: (() -> Unit)? = null - private var batteryReceiverRegistered: Boolean = false + private lateinit var cpuBackend: String + private lateinit var gpuDriver: String + private var _binding: FragmentEmulationBinding? = null private val binding get() = _binding!! @@ -198,8 +209,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { } }) binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED) - binding.inGameMenu.getHeaderView(0).findViewById(R.id.text_game_title).text = - game.title + binding.inGameMenu.getHeaderView(0).apply { + val titleView = findViewById(R.id.text_game_title) + titleView.text = game.title + } binding.inGameMenu.menu.findItem(R.id.menu_lock_drawer).apply { val lockMode = IntSetting.LOCK_DRAWER.getInt() @@ -375,9 +388,23 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { emulationState.updateSurface() // Setup overlays - updateShowFpsOverlay() - val temperature = getBatteryTemperature(requireContext()) - updateThermalOverlay(temperature) + updateshowStatsOvelray() + + // Re update binding when the specs values get initialized properly + binding.inGameMenu.getHeaderView(0).apply { + val titleView = findViewById(R.id.text_game_title) + val cpuBackendLabel = findViewById(R.id.cpu_backend) + val gpuvendorLabel = findViewById(R.id.gpu_vendor) + + titleView.text = game.title + cpuBackendLabel.text = NativeLibrary.getCpuBackend() + gpuvendorLabel.text = NativeLibrary.getGpuDriver() + } + + + val position = IntSetting.PERF_OVERLAY_POSITION.getInt() + updateStatsPosition(position) + } } emulationViewModel.isEmulationStopping.collect(viewLifecycleOwner) { @@ -385,7 +412,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { binding.loadingText.setText(R.string.shutting_down) ViewUtils.showView(binding.loadingIndicator) ViewUtils.hideView(binding.inputContainer) - ViewUtils.hideView(binding.showFpsText) + ViewUtils.hideView(binding.showStatsOverlayText) } } emulationViewModel.drawerOpen.collect(viewLifecycleOwner) { @@ -467,22 +494,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { if (emulationState.isRunning && emulationActivity?.isInPictureInPictureMode != true) { emulationState.pause() } - context?.let { - if (batteryReceiverRegistered) { - it.unregisterReceiver(batteryReceiver) - batteryReceiverRegistered = false - } - } super.onPause() } override fun onDestroyView() { - context?.let { - if (batteryReceiverRegistered) { - it.unregisterReceiver(batteryReceiver) - batteryReceiverRegistered = false - } - } super.onDestroyView() _binding = null } @@ -492,12 +507,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { super.onDetach() } override fun onResume() { - super.onResume() - if (!batteryReceiverRegistered) { - val filter = IntentFilter(Intent.ACTION_BATTERY_CHANGED) - context?.registerReceiver(batteryReceiver, filter) - batteryReceiverRegistered = true - } + super.onResume() + // If the overlay is enabled, we need to update the position if changed + val position = IntSetting.PERF_OVERLAY_POSITION.getInt() + updateStatsPosition(position) } private fun resetInputOverlay() { @@ -508,38 +521,91 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { } } @SuppressLint("DefaultLocale") - private fun updateShowFpsOverlay() { + private fun updateshowStatsOvelray() { val showOverlay = BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean() - binding.showFpsText.setTextColor(Color.parseColor("#A146FF")) - binding.showFpsText.setVisible(showOverlay) + binding.showStatsOverlayText.apply { + setTextColor( + MaterialColors.getColor( + this, + com.google.android.material.R.attr.colorPrimary + ) + ) + } + binding.showStatsOverlayText.setVisible(showOverlay) if (showOverlay) { val SYSTEM_FPS = 0 val FPS = 1 val FRAMETIME = 2 val SPEED = 3 + val sb = StringBuilder() perfStatsUpdater = { if (emulationViewModel.emulationStarted.value && !emulationViewModel.isEmulationStopping.value ) { + sb.setLength(0) // Clear the StringBuilder to avoid recurring appends + val perfStats = NativeLibrary.getPerfStats() - val cpuBackend = NativeLibrary.getCpuBackend() - val gpuDriver = NativeLibrary.getGpuDriver() - - // Get memory info - val mi = ActivityManager.MemoryInfo() - val activityManager = - requireContext().getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager - activityManager.getMemoryInfo(mi) - - // Calculate used memory - val usedMegs = (mi.totalMem - mi.availMem) / 1048576L // Convert bytes to megabytes if (_binding != null) { - binding.showFpsText.text = String.format( - "%.1f FPS • %d MB • %s/%s", - perfStats[FPS], usedMegs, cpuBackend, gpuDriver - ) + if (BooleanSetting.SHOW_FPS.getBoolean(NativeConfig.isPerGameConfigLoaded())) { + sb.append(String.format("FPS: %d", (perfStats[FPS] + 0.5).toInt())) + // perfStats[FPS], usedMegs, cpuBackend, gpuDriver + + if (BooleanSetting.SHOW_FRAMETIME.getBoolean(NativeConfig.isPerGameConfigLoaded())) { + if (sb.isNotEmpty()) sb.append(" | ") + sb.append( + String.format( + "FT: %.1fms", + (perfStats[FRAMETIME] * 1000.0f).toFloat() + ) + ) + } + + if (BooleanSetting.SHOW_SPEED.getBoolean(NativeConfig.isPerGameConfigLoaded())) { + if (sb.isNotEmpty()) sb.append(" | ") + sb.append( + String.format( + "Speed: %d%%", + (perfStats[SPEED] * 100.0 + 0.5).toInt() + ) + ) + } + + if (BooleanSetting.SHOW_APP_RAM_USAGE.getBoolean(NativeConfig.isPerGameConfigLoaded())) { + if (sb.isNotEmpty()) sb.append(" | ") + val appRamUsage = + File("/proc/self/statm").readLines()[0].split(' ')[1].toLong() * 4096 / 1000000 + sb.append("Process RAM: $appRamUsage MB") + } + + if (BooleanSetting.SHOW_SYSTEM_RAM_USAGE.getBoolean(NativeConfig.isPerGameConfigLoaded())) { + if (sb.isNotEmpty()) sb.append(" | ") + context?.let { ctx -> + val activityManager = + ctx.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager + val memInfo = ActivityManager.MemoryInfo() + activityManager.getMemoryInfo(memInfo) + val usedRamMB = (memInfo.totalMem - memInfo.availMem) / 1048576L + sb.append("RAM: $usedRamMB MB") + } + } + + if (BooleanSetting.SHOW_BAT_TEMPERATURE.getBoolean(NativeConfig.isPerGameConfigLoaded())) { + if (sb.isNotEmpty()) sb.append(" | ") + val batteryTemp = getBatteryTemperature() + val tempF = celsiusToFahrenheit(batteryTemp) + sb.append(String.format("%.1f°C/%.1f°F", batteryTemp, tempF)) + } + + if (BooleanSetting.OVERLAY_BACKGROUND.getBoolean(NativeConfig.isPerGameConfigLoaded())) { + binding.showStatsOverlayText.setBackgroundResource(R.color.yuzu_transparent_black) + } else { + binding.showStatsOverlayText.setBackgroundResource(0) + } + + binding.showStatsOverlayText.text = sb.toString() } + } perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 800) } } @@ -551,47 +617,76 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { } } -private val batteryReceiver = object : BroadcastReceiver() { - override fun onReceive(context: Context?, intent: Intent?) { - intent?.let { - if (it.action == Intent.ACTION_BATTERY_CHANGED) { - val temperature = getBatteryTemperature(context!!) - updateThermalOverlay(temperature) + private fun updateStatsPosition(position: Int) { + val params = binding.showStatsOverlayText.layoutParams as FrameLayout.LayoutParams + when (position) { + 0 -> { + params.gravity = (Gravity.TOP or Gravity.START) + params.setMargins(resources.getDimensionPixelSize(R.dimen.spacing_large), 0, 0, 0) + } + + 1 -> { + params.gravity = (Gravity.TOP or Gravity.CENTER_HORIZONTAL) + } + + 2 -> { + params.gravity = (Gravity.TOP or Gravity.END) + params.setMargins(0, 0, resources.getDimensionPixelSize(R.dimen.spacing_large), 0) + } + + 3 -> { + params.gravity = (Gravity.BOTTOM or Gravity.START) + params.setMargins(resources.getDimensionPixelSize(R.dimen.spacing_large), 0, 0, 0) + } + + 4 -> { + params.gravity = (Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL) + } + + 5 -> { + params.gravity = (Gravity.BOTTOM or Gravity.END) + params.setMargins(0, 0, resources.getDimensionPixelSize(R.dimen.spacing_large), 0) } } } -} -private fun updateThermalOverlay(temperature: Float) { - if (BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean() && - emulationViewModel.emulationStarted.value && - !emulationViewModel.isEmulationStopping.value - ) { - // Convert to Fahrenheit - val fahrenheit = (temperature * 9f / 5f) + 32f - - // Determine color based on temperature ranges - val color = when { - temperature < 35 -> Color.parseColor("#00C8FF") - temperature < 40 -> Color.parseColor("#A146FF") - temperature < 45 -> Color.parseColor("#FFA500") - else -> Color.RED + 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 } - - binding.showThermalsText.setTextColor(color) - binding.showThermalsText.text = String.format("%.1f°C • %.1f°F", temperature, fahrenheit) } -} - -private fun getBatteryTemperature(context: Context): Float { - val intent: Intent? = context.registerReceiver( - null, - IntentFilter(Intent.ACTION_BATTERY_CHANGED) - ) - val temperature = intent?.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 0) ?: 0 - return temperature / 10.0f + private fun celsiusToFahrenheit(celsius: Float): Float { + return (celsius * 9 / 5) + 32 } + private fun updateThermalOverlay(temperature: Float) { + if (BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean() && + emulationViewModel.emulationStarted.value && + !emulationViewModel.isEmulationStopping.value + ) { + // Convert to Fahrenheit + val fahrenheit = (temperature * 9f / 5f) + 32f + + // Determine color based on temperature ranges + val color = when { + temperature < 35 -> Color.parseColor("#00C8FF") + temperature < 40 -> Color.parseColor("#A146FF") + temperature < 45 -> Color.parseColor("#FFA500") + else -> Color.RED + } + + binding.showThermalsText.setTextColor(color) + binding.showThermalsText.text = String.format("%.1f°C • %.1f°F", temperature, fahrenheit) + } + } + + @SuppressLint("SourceLockedOrientationActivity") private fun updateOrientation() { emulationActivity?.let { @@ -717,10 +812,8 @@ private fun getBatteryTemperature(context: Context): Float { popup.menuInflater.inflate(R.menu.menu_overlay_options, popup.menu) popup.menu.apply { - findItem(R.id.menu_toggle_fps).isChecked = + findItem(R.id.menu_show_stats_overlay).isChecked = BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean() - findItem(R.id.thermal_indicator).isChecked = - BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean() findItem(R.id.menu_rel_stick_center).isChecked = BooleanSetting.JOYSTICK_REL_CENTER.getBoolean() findItem(R.id.menu_dpad_slide).isChecked = BooleanSetting.DPAD_SLIDE.getBoolean() @@ -733,34 +826,12 @@ private fun getBatteryTemperature(context: Context): Float { popup.setOnDismissListener { NativeConfig.saveGlobalConfig() } popup.setOnMenuItemClickListener { when (it.itemId) { - R.id.menu_toggle_fps -> { + R.id.menu_show_stats_overlay -> { it.isChecked = !it.isChecked BooleanSetting.SHOW_PERFORMANCE_OVERLAY.setBoolean(it.isChecked) - updateShowFpsOverlay() + updateshowStatsOvelray() true } - - R.id.thermal_indicator -> { - it.isChecked = !it.isChecked - BooleanSetting.SHOW_THERMAL_OVERLAY.setBoolean(it.isChecked) - if (it.isChecked) { - val temperature = getBatteryTemperature(requireContext()) - updateThermalOverlay(temperature) - if (!batteryReceiverRegistered) { - val filter = IntentFilter(Intent.ACTION_BATTERY_CHANGED) - context?.registerReceiver(batteryReceiver, filter) - batteryReceiverRegistered = true - } - } else { - if (batteryReceiverRegistered) { - context?.unregisterReceiver(batteryReceiver) - batteryReceiverRegistered = false - } - binding.showThermalsText.text = "" - } - true - } - R.id.menu_edit_overlay -> { binding.drawerLayout.close() binding.surfaceInputOverlay.requestFocus() @@ -951,7 +1022,8 @@ private fun getBatteryTemperature(context: Context): Float { right = cutInsets.right } - v.updatePadding(left = left, top = cutInsets.top, right = right) + v.setPadding(left, cutInsets.top, right, 0) + windowInsets } } diff --git a/src/android/app/src/main/jni/android_settings.h b/src/android/app/src/main/jni/android_settings.h index 00baf86a9b..a47803604a 100644 --- a/src/android/app/src/main/jni/android_settings.h +++ b/src/android/app/src/main/jni/android_settings.h @@ -66,9 +66,23 @@ struct Values { Settings::Setting haptic_feedback{linkage, true, "haptic_feedback", Settings::Category::Overlay}; Settings::Setting show_performance_overlay{linkage, true, "show_performance_overlay", - Settings::Category::Overlay}; - Settings::Setting show_thermal_overlay{linkage, false, "show_thermal_overlay", - Settings::Category::Overlay}; + Settings::Category::Overlay, Settings::Specialization::Paired, true , true}; + Settings::Setting overlay_background{linkage, false, "overlay_background", + Settings::Category::Overlay, Settings::Specialization::Default, true , true, &show_performance_overlay}; + Settings::Setting perf_overlay_position{linkage, 0, "perf_overlay_position", + Settings::Category::Overlay, Settings::Specialization::Default, true , true, &show_performance_overlay}; + Settings::Setting show_fps{linkage, true, "show_fps", + Settings::Category::Overlay, Settings::Specialization::Default, true , true, &show_performance_overlay}; + Settings::Setting show_frame_time{linkage, false, "show_frame_time", + Settings::Category::Overlay, Settings::Specialization::Default, true , true, &show_performance_overlay}; + Settings::Setting show_speed{linkage, true, "show_speed", + Settings::Category::Overlay, Settings::Specialization::Default, true , true, &show_performance_overlay}; + Settings::Setting show_app_ram_usage{linkage, false, "show_app_ram_usage", + Settings::Category::Overlay, Settings::Specialization::Default, true , true, &show_performance_overlay}; + Settings::Setting show_system_ram_usage{linkage, false, "show_system_ram_usage", + Settings::Category::Overlay, Settings::Specialization::Default, true , true, &show_performance_overlay}; + Settings::Setting show_bat_temperature{linkage, false, "show_bat_temperature", + Settings::Category::Overlay, Settings::Specialization::Default, true , true, &show_performance_overlay}; Settings::Setting show_input_overlay{linkage, true, "show_input_overlay", Settings::Category::Overlay}; Settings::Setting touchscreen{linkage, true, "touchscreen", Settings::Category::Overlay}; diff --git a/src/android/app/src/main/res/drawable/ic_frames.xml b/src/android/app/src/main/res/drawable/ic_frames.xml new file mode 100644 index 0000000000..aee24007b0 --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_frames.xml @@ -0,0 +1,32 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/android/app/src/main/res/layout/fragment_emulation.xml b/src/android/app/src/main/res/layout/fragment_emulation.xml index 185ad37814..00f2cdc103 100644 --- a/src/android/app/src/main/res/layout/fragment_emulation.xml +++ b/src/android/app/src/main/res/layout/fragment_emulation.xml @@ -140,15 +140,13 @@ android:id="@+id/overlay_container" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_marginHorizontal="20dp" - android:fitsSystemWindows="true"> + android:fitsSystemWindows="false"> - + android:layout_marginEnd="24dp"> + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/android/app/src/main/res/menu/menu_overlay_options.xml b/src/android/app/src/main/res/menu/menu_overlay_options.xml index a9e807427b..3315dbfdc9 100644 --- a/src/android/app/src/main/res/menu/menu_overlay_options.xml +++ b/src/android/app/src/main/res/menu/menu_overlay_options.xml @@ -2,13 +2,8 @@ - - #B7B7B7 + #80000000 #C6C5D0 #FFB4AB #93000A diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index 99013e0485..318baacf09 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml @@ -183,6 +183,23 @@ 2 + + @string/overlay_position_top_left + @string/overlay_position_center_top + @string/overlay_position_top_right + @string/overlay_position_bottom_left + @string/overlay_position_center_bottom + @string/overlay_position_bottom_right + + + 0 + 1 + 2 + 3 + 4 + 5 + + @string/cpu_backend_dynarmic @string/cpu_backend_nce diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index bb0b5c58dc..3606ff9d2a 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -9,6 +9,37 @@ Shows notifications when something goes wrong. Notification permission not granted! + + ShoW Performance Stats Overlay + Customization + Visibility + Overlay + Enable Performance Stats Overlay + Configure what information is shown in the performance stats overlay + Show FPS + Display current frames per second + Show Frametime + Display current frametime + Show Speed + Display current emulation speed percentage + Show App Memory Usage + Display the amount of RAM getting used by the emulator + Show System Memory Usage + Display the amount of RAM getting used by the system + Show Battery Temperature + Display current Battery temperature in Celsius and Fahrenheit + Overlay Position + Choose where the performance stats overlay is displayed on the screen + Top Left + Top Right + Bottom Left + Bottom Right + Center Top + Center Bottom + Overlay Background + Adds a background behind the overlay for easier reading + + Welcome! Learn how to setup <b>eden</b> and jump into emulation. diff --git a/src/android/app/src/main/res/values/yuzu_colors.xml b/src/android/app/src/main/res/values/yuzu_colors.xml index a45b95f85c..a5af0886a9 100644 --- a/src/android/app/src/main/res/values/yuzu_colors.xml +++ b/src/android/app/src/main/res/values/yuzu_colors.xml @@ -229,6 +229,7 @@ #410002 #000000 #000000 + #80000000 #FFFFFF #FFFFFF