diff options
author | Zephyron <zephyron@citron-emu.org> | 2025-02-01 19:18:35 +1000 |
---|---|---|
committer | Zephyron <zephyron@citron-emu.org> | 2025-02-01 19:18:35 +1000 |
commit | 0216eaa071bf76172804ca3de2fb9c8ea32279e4 (patch) | |
tree | 0d8906791ab2314d129a84043ec7afaf72687c2b /src | |
parent | 137034ca2c800d43a48c903a8f90ff8fc879fb9a (diff) |
feat: Add RAM usage overlay and improve thermal display
- Add new RAM usage overlay showing current/max memory usage with color gradient
- Simplify thermal overlay to show temperature in C/F with color indication
- Add SHOW_RAM_OVERLAY boolean setting (disabled by default in UI)
- Set default values for thermal overlay and black backgrounds to true
- Update copyright headers to include Citron Emulator Project
The RAM overlay displays native heap usage with color gradient from green
(low usage) to red (high usage). The thermal display was simplified to show
just temperatures while maintaining the color indication based on system
thermal status.
Diffstat (limited to 'src')
6 files changed, 95 insertions, 26 deletions
diff --git a/src/android/app/src/main/java/org/citron/citron_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/citron/citron_emu/features/settings/model/BooleanSetting.kt index 895194db5..f781d10c1 100644 --- a/src/android/app/src/main/java/org/citron/citron_emu/features/settings/model/BooleanSetting.kt +++ b/src/android/app/src/main/java/org/citron/citron_emu/features/settings/model/BooleanSetting.kt @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-FileCopyrightText: 2025 Citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later package org.citron.citron_emu.features.settings.model @@ -27,6 +28,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting { SHOW_INPUT_OVERLAY("show_input_overlay"), TOUCHSCREEN("touchscreen"), SHOW_THERMAL_OVERLAY("show_thermal_overlay"), + SHOW_RAM_OVERLAY("show_ram_overlay"), USE_AUTO_STUB("use_auto_stub"); override fun getBoolean(needsGlobal: Boolean): Boolean = diff --git a/src/android/app/src/main/java/org/citron/citron_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/citron/citron_emu/fragments/EmulationFragment.kt index 93a15def9..f4678b603 100644 --- a/src/android/app/src/main/java/org/citron/citron_emu/fragments/EmulationFragment.kt +++ b/src/android/app/src/main/java/org/citron/citron_emu/fragments/EmulationFragment.kt @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-FileCopyrightText: 2025 Citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later package org.citron.citron_emu.fragments @@ -11,9 +12,11 @@ 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.Debug import android.os.Handler import android.os.Looper import android.os.PowerManager @@ -68,6 +71,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { private var emulationActivity: EmulationActivity? = null private var perfStatsUpdater: (() -> Unit)? = null private var thermalStatsUpdater: (() -> Unit)? = null + private var ramStatsUpdater: (() -> Unit)? = null private var _binding: FragmentEmulationBinding? = null private val binding get() = _binding!! @@ -83,6 +87,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { private lateinit var powerManager: PowerManager + private val ramStatsUpdateHandler = Handler(Looper.myLooper()!!) + override fun onAttach(context: Context) { super.onAttach(context) if (context is EmulationActivity) { @@ -376,6 +382,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { // Setup overlays updateShowFpsOverlay() updateThermalOverlay() + updateRamOverlay() } } emulationViewModel.isEmulationStopping.collect(viewLifecycleOwner) { @@ -470,6 +477,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { override fun onDestroyView() { super.onDestroyView() + if (ramStatsUpdater != null) { + ramStatsUpdateHandler.removeCallbacks(ramStatsUpdater!!) + } _binding = null } @@ -552,8 +562,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { emulationViewModel.emulationStarted.value && !emulationViewModel.isEmulationStopping.value ) { - // Get thermal status + // Get thermal status for color val thermalStatus = when (powerManager.currentThermalStatus) { + PowerManager.THERMAL_STATUS_NONE -> 0f PowerManager.THERMAL_STATUS_LIGHT -> 0.25f PowerManager.THERMAL_STATUS_MODERATE -> 0.5f PowerManager.THERMAL_STATUS_SEVERE -> 0.75f @@ -563,34 +574,57 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { else -> 0f } - // Convert to Fahrenheit for additional info + // Convert to Fahrenheit val fahrenheit = (temperature * 9f / 5f) + 32f - // Create progress bar using block elements - val progressBarLength = 12 - val filledBars = (thermalStatus * progressBarLength).toInt() - val progressBar = buildString { - append("│") // Left border - repeat(filledBars) { append("█") } - repeat(progressBarLength - filledBars) { append("░") } - append("│") // Right border - append(" ") - append(String.format("%3d%%", (thermalStatus * 100).toInt())) - } - - // Color interpolation based on temperature (green at 30°C, red at 45°C) - val normalizedTemp = ((temperature - 30f) / 15f).coerceIn(0f, 1f) - val red = (normalizedTemp * 255).toInt() - val green = ((1f - normalizedTemp) * 255).toInt() + // Color based on thermal status (green to red) + val red = (thermalStatus * 255).toInt() + val green = ((1f - thermalStatus) * 255).toInt() val color = android.graphics.Color.rgb(red, green, 0) binding.showThermalsText.setTextColor(color) - binding.showThermalsText.text = String.format( - "%s\n%.1f°C • %.1f°F", - progressBar, - temperature, - fahrenheit - ) + binding.showThermalsText.text = String.format("%.1f°C • %.1f°F", temperature, fahrenheit) + } + } + + private fun updateRamOverlay() { + val showOverlay = BooleanSetting.SHOW_RAM_OVERLAY.getBoolean() + binding.showRamText.setVisible(showOverlay) + if (showOverlay) { + ramStatsUpdater = { + if (emulationViewModel.emulationStarted.value && + !emulationViewModel.isEmulationStopping.value + ) { + val runtime = Runtime.getRuntime() + val nativeHeapSize = Debug.getNativeHeapSize() + val nativeHeapFreeSize = Debug.getNativeHeapFreeSize() + val nativeHeapUsed = nativeHeapSize - nativeHeapFreeSize + + val usedMemInMB = nativeHeapUsed / 1048576L + val maxMemInMB = nativeHeapSize / 1048576L + val percentUsed = (nativeHeapUsed.toFloat() / nativeHeapSize.toFloat() * 100f) + + // Color interpolation from green to red based on usage percentage + val normalizedUsage = (percentUsed / 100f).coerceIn(0f, 1f) + val red = (normalizedUsage * 255).toInt() + val green = ((1f - normalizedUsage) * 255).toInt() + val color = Color.rgb(red, green, 0) + + binding.showRamText.setTextColor(color) + binding.showRamText.text = String.format( + "\nRAM: %d/%d MB (%.1f%%)", + usedMemInMB, + maxMemInMB, + percentUsed + ) + ramStatsUpdateHandler.postDelayed(ramStatsUpdater!!, 1000) + } + } + ramStatsUpdateHandler.post(ramStatsUpdater!!) + } else { + if (ramStatsUpdater != null) { + ramStatsUpdateHandler.removeCallbacks(ramStatsUpdater!!) + } } } @@ -723,6 +757,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean() findItem(R.id.thermal_indicator).isChecked = BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean() + findItem(R.id.ram_meter).apply { + isChecked = BooleanSetting.SHOW_RAM_OVERLAY.getBoolean() + isEnabled = false // This grays out the option + } findItem(R.id.menu_rel_stick_center).isChecked = BooleanSetting.JOYSTICK_REL_CENTER.getBoolean() findItem(R.id.menu_dpad_slide).isChecked = BooleanSetting.DPAD_SLIDE.getBoolean() @@ -749,6 +787,11 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { true } + R.id.ram_meter -> { + // Do nothing since it's disabled + true + } + R.id.menu_edit_overlay -> { binding.drawerLayout.close() binding.surfaceInputOverlay.requestFocus() diff --git a/src/android/app/src/main/jni/android_settings.h b/src/android/app/src/main/jni/android_settings.h index 00baf86a9..2dba36990 100644 --- a/src/android/app/src/main/jni/android_settings.h +++ b/src/android/app/src/main/jni/android_settings.h @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-FileCopyrightText: 2025 Citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -51,7 +52,7 @@ struct Values { Settings::Setting<s32> theme{linkage, 0, "theme", Settings::Category::Android}; Settings::Setting<s32> theme_mode{linkage, -1, "theme_mode", Settings::Category::Android}; - Settings::Setting<bool> black_backgrounds{linkage, false, "black_backgrounds", + Settings::Setting<bool> black_backgrounds{linkage, true, "black_backgrounds", Settings::Category::Android}; // Input/performance overlay settings @@ -67,8 +68,10 @@ struct Values { Settings::Category::Overlay}; Settings::Setting<bool> show_performance_overlay{linkage, true, "show_performance_overlay", Settings::Category::Overlay}; - Settings::Setting<bool> show_thermal_overlay{linkage, false, "show_thermal_overlay", + Settings::Setting<bool> show_thermal_overlay{linkage, true, "show_thermal_overlay", Settings::Category::Overlay}; + Settings::Setting<bool> show_ram_overlay{linkage, true, "show_ram_overlay", + Settings::Category::Overlay}; Settings::Setting<bool> show_input_overlay{linkage, true, "show_input_overlay", Settings::Category::Overlay}; Settings::Setting<bool> touchscreen{linkage, true, "touchscreen", Settings::Category::Overlay}; 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 df91a08c5..fe028e028 100644 --- a/src/android/app/src/main/res/layout/fragment_emulation.xml +++ b/src/android/app/src/main/res/layout/fragment_emulation.xml @@ -171,6 +171,21 @@ </FrameLayout> + <com.google.android.material.textview.MaterialTextView + android:id="@+id/show_ram_text" + style="@style/TextAppearance.Material3.BodySmall" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="left" + android:layout_marginTop="24dp" + android:clickable="false" + android:focusable="false" + android:textColor="@android:color/white" + android:shadowColor="@android:color/black" + android:shadowRadius="3" + android:layout_below="@id/show_fps_text" + tools:ignore="RtlHardcoded" /> + </androidx.coordinatorlayout.widget.CoordinatorLayout> <com.google.android.material.navigation.NavigationView 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 a9e807427..d601421a7 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 @@ -12,6 +12,11 @@ android:checkable="true" /> <item + android:id="@+id/ram_meter" + android:title="@string/emulation_ram_meter" + android:checkable="true" /> + + <item android:id="@+id/menu_edit_overlay" android:title="@string/emulation_touch_overlay_edit" /> diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 5133fa98e..ce2b21bf1 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -480,6 +480,7 @@ <string name="emulation_done">Done</string> <string name="emulation_fps_counter">FPS counter</string> <string name="emulation_thermal_indicator">Thermal indicator</string> + <string name="emulation_ram_meter">RAM meter</string> <string name="emulation_toggle_controls">Toggle controls</string> <string name="emulation_rel_stick_center">Relative stick center</string> <string name="emulation_dpad_slide">D-pad slide</string> |