Merge branch 'android-advanced-emulation-settings' into 'master'

cmake(android): Optimize Vulkan Validation Layer handling

See merge request citron/rewrite!21
This commit is contained in:
Zephyron
2025-05-30 07:38:33 +00:00
17 changed files with 807 additions and 73 deletions

View File

@@ -85,19 +85,28 @@ option(ENABLE_OPENSSL "Enable OpenSSL backend for ISslConnection" ${DEFAULT_ENAB
if (ANDROID AND CITRON_DOWNLOAD_ANDROID_VVL) if (ANDROID AND CITRON_DOWNLOAD_ANDROID_VVL)
set(vvl_version "sdk-1.3.261.1") set(vvl_version "sdk-1.3.261.1")
set(vvl_zip_file "${CMAKE_BINARY_DIR}/externals/vvl-android.zip") set(vvl_zip_file "${CMAKE_BINARY_DIR}/externals/vvl-android.zip")
if (NOT EXISTS "${vvl_zip_file}")
# Download and extract validation layer release to externals directory
set(vvl_base_url "https://github.com/KhronosGroup/Vulkan-ValidationLayers/releases/download")
file(DOWNLOAD "${vvl_base_url}/${vvl_version}/android-binaries-${vvl_version}-android.zip"
"${vvl_zip_file}" SHOW_PROGRESS)
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf "${vvl_zip_file}"
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/externals")
endif()
# Copy the arm64 binary to src/android/app/main/jniLibs
set(vvl_lib_path "${CMAKE_CURRENT_SOURCE_DIR}/src/android/app/src/main/jniLibs/arm64-v8a/") set(vvl_lib_path "${CMAKE_CURRENT_SOURCE_DIR}/src/android/app/src/main/jniLibs/arm64-v8a/")
file(COPY "${CMAKE_BINARY_DIR}/externals/android-binaries-${vvl_version}/arm64-v8a/libVkLayer_khronos_validation.so" set(vvl_lib_file "${vvl_lib_path}/libVkLayer_khronos_validation.so")
DESTINATION "${vvl_lib_path}")
# Only download and extract if the final library file doesn't exist
if (NOT EXISTS "${vvl_lib_file}")
if (NOT EXISTS "${vvl_zip_file}")
# Download validation layer release to externals directory
set(vvl_base_url "https://github.com/KhronosGroup/Vulkan-ValidationLayers/releases/download")
file(DOWNLOAD "${vvl_base_url}/${vvl_version}/android-binaries-${vvl_version}-android.zip"
"${vvl_zip_file}" SHOW_PROGRESS)
endif()
# Extract if not already extracted
if (NOT EXISTS "${CMAKE_BINARY_DIR}/externals/android-binaries-${vvl_version}")
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf "${vvl_zip_file}"
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/externals")
endif()
# Copy the arm64 binary to src/android/app/main/jniLibs
file(COPY "${CMAKE_BINARY_DIR}/externals/android-binaries-${vvl_version}/arm64-v8a/libVkLayer_khronos_validation.so"
DESTINATION "${vvl_lib_path}")
endif()
endif() endif()
if (ANDROID) if (ANDROID)

View File

@@ -28,8 +28,8 @@ val autoVersion = (((System.currentTimeMillis() / 1000) - 1451606400) / 10).toIn
android { android {
namespace = "org.citron.citron_emu" namespace = "org.citron.citron_emu"
compileSdkVersion = "android-34" compileSdkVersion = "android-35"
ndkVersion = "26.1.10909125" ndkVersion = "27.2.12479018" // "26.1.10909125"
buildFeatures { buildFeatures {
viewBinding = true viewBinding = true
@@ -57,7 +57,8 @@ android {
// TODO If this is ever modified, change application_id in strings.xml // TODO If this is ever modified, change application_id in strings.xml
applicationId = "org.citron.citron_emu" applicationId = "org.citron.citron_emu"
minSdk = 30 minSdk = 30
targetSdk = 34 //noinspection EditedTargetSdkVersion
targetSdk = 35
versionName = getGitVersion() versionName = getGitVersion()
versionCode = if (System.getenv("AUTO_VERSIONED") == "true") { versionCode = if (System.getenv("AUTO_VERSIONED") == "true") {
@@ -106,12 +107,12 @@ android {
resValue("string", "app_name_suffixed", "citron") resValue("string", "app_name_suffixed", "citron")
isDefault = true isDefault = true
// isShrinkResources = true isShrinkResources = true
isMinifyEnabled = true isMinifyEnabled = true
isDebuggable = false isDebuggable = false
// isJniDebuggable = false isJniDebuggable = false
proguardFiles( proguardFiles(
getDefaultProguardFile("proguard-android.txt"), getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro" "proguard-rules.pro"
) )
} }
@@ -124,7 +125,7 @@ android {
isMinifyEnabled = true isMinifyEnabled = true
isDebuggable = true isDebuggable = true
proguardFiles( proguardFiles(
getDefaultProguardFile("proguard-android.txt"), getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro" "proguard-rules.pro"
) )
versionNameSuffix = "-relWithDebInfo" versionNameSuffix = "-relWithDebInfo"
@@ -161,7 +162,7 @@ android {
externalNativeBuild { externalNativeBuild {
cmake { cmake {
version = "3.22.1" version = "3.31.7"
path = file("../../../CMakeLists.txt") path = file("../../../CMakeLists.txt")
} }
} }
@@ -178,10 +179,12 @@ android {
"-DCITRON_USE_BUNDLED_VCPKG=ON", "-DCITRON_USE_BUNDLED_VCPKG=ON",
"-DCITRON_USE_BUNDLED_FFMPEG=ON", "-DCITRON_USE_BUNDLED_FFMPEG=ON",
"-DCITRON_ENABLE_LTO=ON", "-DCITRON_ENABLE_LTO=ON",
"-DCMAKE_EXPORT_COMPILE_COMMANDS=ON" "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON",
"-DCMAKE_POLICY_VERSION_MINIMUM=3.5",
"-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON",
) )
abiFilters("arm64-v8a", "x86_64") abiFilters("arm64-v8a") // , "x86_64")
} }
} }
} }
@@ -239,7 +242,6 @@ dependencies {
implementation("io.coil-kt:coil:2.2.2") implementation("io.coil-kt:coil:2.2.2")
implementation("androidx.core:core-splashscreen:1.0.1") implementation("androidx.core:core-splashscreen:1.0.1")
implementation("androidx.window:window:1.2.0-beta03") implementation("androidx.window:window:1.2.0-beta03")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
implementation("androidx.navigation:navigation-fragment-ktx:2.7.4") implementation("androidx.navigation:navigation-fragment-ktx:2.7.4")
implementation("androidx.navigation:navigation-ui-ktx:2.7.4") implementation("androidx.navigation:navigation-ui-ktx:2.7.4")

View File

@@ -1,4 +1,5 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project // SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-FileCopyrightText: 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
package org.citron.citron_emu.features.settings.model package org.citron.citron_emu.features.settings.model
@@ -26,7 +27,8 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
SHOW_PERFORMANCE_OVERLAY("show_performance_overlay"), SHOW_PERFORMANCE_OVERLAY("show_performance_overlay"),
SHOW_INPUT_OVERLAY("show_input_overlay"), SHOW_INPUT_OVERLAY("show_input_overlay"),
TOUCHSCREEN("touchscreen"), TOUCHSCREEN("touchscreen"),
SHOW_THERMAL_OVERLAY("show_thermal_overlay"); SHOW_THERMAL_OVERLAY("show_thermal_overlay"),
SHOW_RAM_METER("show_ram_meter");
override fun getBoolean(needsGlobal: Boolean): Boolean = override fun getBoolean(needsGlobal: Boolean): Boolean =
NativeConfig.getBoolean(key, needsGlobal) NativeConfig.getBoolean(key, needsGlobal)

View File

@@ -1,4 +1,5 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project // SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-FileCopyrightText: 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
package org.citron.citron_emu.features.settings.model package org.citron.citron_emu.features.settings.model
@@ -26,7 +27,14 @@ enum class IntSetting(override val key: String) : AbstractIntSetting {
OVERLAY_OPACITY("control_opacity"), OVERLAY_OPACITY("control_opacity"),
LOCK_DRAWER("lock_drawer"), LOCK_DRAWER("lock_drawer"),
VERTICAL_ALIGNMENT("vertical_alignment"), VERTICAL_ALIGNMENT("vertical_alignment"),
FSR_SHARPENING_SLIDER("fsr_sharpening_slider"); FSR_SHARPENING_SLIDER("fsr_sharpening_slider"),
// Zep Zone settings
MEMORY_LAYOUT_MODE("memory_layout_mode"),
ASTC_DECODE_MODE("accelerate_astc"),
ASTC_RECOMPRESSION("astc_recompression"),
SHADER_BACKEND("shader_backend"),
VRAM_USAGE_MODE("vram_usage_mode");
override fun getInt(needsGlobal: Boolean): Int = NativeConfig.getInt(key, needsGlobal) override fun getInt(needsGlobal: Boolean): Int = NativeConfig.getInt(key, needsGlobal)

View File

@@ -1,4 +1,5 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project // SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-FileCopyrightText: 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
package org.citron.citron_emu.features.settings.model package org.citron.citron_emu.features.settings.model
@@ -22,7 +23,8 @@ object Settings {
SECTION_INPUT_PLAYER_SEVEN, SECTION_INPUT_PLAYER_SEVEN,
SECTION_INPUT_PLAYER_EIGHT, SECTION_INPUT_PLAYER_EIGHT,
SECTION_THEME(R.string.preferences_theme), SECTION_THEME(R.string.preferences_theme),
SECTION_DEBUG(R.string.preferences_debug); SECTION_DEBUG(R.string.preferences_debug),
SECTION_ZEP_ZONE(R.string.preferences_zep_zone);
} }
fun getPlayerString(player: Int): String = fun getPlayerString(player: Int): String =

View File

@@ -1,4 +1,5 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project // SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-FileCopyrightText: 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
package org.citron.citron_emu.features.settings.model.view package org.citron.citron_emu.features.settings.model.view
@@ -386,6 +387,53 @@ abstract class SettingsItem(
override fun reset() = setBoolean(defaultValue) override fun reset() = setBoolean(defaultValue)
} }
put(SwitchSetting(fastmem, R.string.fastmem)) put(SwitchSetting(fastmem, R.string.fastmem))
// Zep Zone Settings
put(
SingleChoiceSetting(
IntSetting.MEMORY_LAYOUT_MODE,
titleId = R.string.memory_layout_mode,
descriptionId = R.string.memory_layout_mode_description,
choicesId = R.array.memoryLayoutNames,
valuesId = R.array.memoryLayoutValues
)
)
put(
SingleChoiceSetting(
IntSetting.ASTC_DECODE_MODE,
titleId = R.string.astc_decode_mode,
descriptionId = R.string.astc_decode_mode_description,
choicesId = R.array.astcDecodeModeNames,
valuesId = R.array.astcDecodeModeValues
)
)
put(
SingleChoiceSetting(
IntSetting.ASTC_RECOMPRESSION,
titleId = R.string.astc_recompression,
descriptionId = R.string.astc_recompression_description,
choicesId = R.array.astcRecompressionNames,
valuesId = R.array.astcRecompressionValues
)
)
put(
SingleChoiceSetting(
IntSetting.SHADER_BACKEND,
titleId = R.string.shader_backend,
descriptionId = R.string.shader_backend_description,
choicesId = R.array.shaderBackendNames,
valuesId = R.array.shaderBackendValues
)
)
put(
SingleChoiceSetting(
IntSetting.VRAM_USAGE_MODE,
titleId = R.string.vram_usage_mode,
descriptionId = R.string.vram_usage_mode_description,
choicesId = R.array.vramUsageModeNames,
valuesId = R.array.vramUsageModeValues
)
)
} }
} }
} }

View File

@@ -1,4 +1,5 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project // SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-FileCopyrightText: 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
package org.citron.citron_emu.features.settings.ui package org.citron.citron_emu.features.settings.ui
@@ -98,6 +99,7 @@ class SettingsFragmentPresenter(
MenuTag.SECTION_INPUT_PLAYER_EIGHT -> addInputPlayer(sl, 7) MenuTag.SECTION_INPUT_PLAYER_EIGHT -> addInputPlayer(sl, 7)
MenuTag.SECTION_THEME -> addThemeSettings(sl) MenuTag.SECTION_THEME -> addThemeSettings(sl)
MenuTag.SECTION_DEBUG -> addDebugSettings(sl) MenuTag.SECTION_DEBUG -> addDebugSettings(sl)
MenuTag.SECTION_ZEP_ZONE -> addZepZoneSettings(sl)
} }
settingsList = sl settingsList = sl
adapter.submitList(settingsList) { adapter.submitList(settingsList) {
@@ -141,6 +143,14 @@ class SettingsFragmentPresenter(
menuKey = MenuTag.SECTION_DEBUG menuKey = MenuTag.SECTION_DEBUG
) )
) )
add(
SubmenuSetting(
titleId = R.string.preferences_zep_zone,
descriptionId = R.string.preferences_zep_zone_description,
iconId = R.drawable.ic_settings,
menuKey = MenuTag.SECTION_ZEP_ZONE
)
)
add( add(
RunnableSetting( RunnableSetting(
titleId = R.string.reset_to_default, titleId = R.string.reset_to_default,
@@ -972,4 +982,19 @@ class SettingsFragmentPresenter(
add(SettingsItem.FASTMEM_COMBINED) add(SettingsItem.FASTMEM_COMBINED)
} }
} }
private fun addZepZoneSettings(sl: ArrayList<SettingsItem>) {
sl.apply {
add(HeaderSetting(R.string.memory_layout_header))
add(IntSetting.MEMORY_LAYOUT_MODE.key)
add(HeaderSetting(R.string.astc_settings_header))
add(IntSetting.ASTC_DECODE_MODE.key)
add(IntSetting.ASTC_RECOMPRESSION.key)
add(HeaderSetting(R.string.advanced_graphics_header))
add(IntSetting.SHADER_BACKEND.key)
add(IntSetting.VRAM_USAGE_MODE.key)
}
}
} }

View File

@@ -8,9 +8,12 @@ import android.annotation.SuppressLint
import android.app.AlertDialog import android.app.AlertDialog
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.pm.ActivityInfo import android.content.pm.ActivityInfo
import android.content.res.Configuration import android.content.res.Configuration
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
@@ -66,6 +69,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
private var emulationActivity: EmulationActivity? = null private var emulationActivity: EmulationActivity? = null
private var perfStatsUpdater: (() -> Unit)? = null private var perfStatsUpdater: (() -> Unit)? = null
private var thermalStatsUpdater: (() -> Unit)? = null private var thermalStatsUpdater: (() -> Unit)? = null
private var ramStatsUpdater: (() -> Unit)? = null
private var _binding: FragmentEmulationBinding? = null private var _binding: FragmentEmulationBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
@@ -374,6 +378,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
// Setup overlays // Setup overlays
updateShowFpsOverlay() updateShowFpsOverlay()
updateThermalOverlay() updateThermalOverlay()
updateRamMeterOverlay()
} }
} }
emulationViewModel.isEmulationStopping.collect(viewLifecycleOwner) { emulationViewModel.isEmulationStopping.collect(viewLifecycleOwner) {
@@ -381,7 +386,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
binding.loadingText.setText(R.string.shutting_down) binding.loadingText.setText(R.string.shutting_down)
ViewUtils.showView(binding.loadingIndicator) ViewUtils.showView(binding.loadingIndicator)
ViewUtils.hideView(binding.inputContainer) ViewUtils.hideView(binding.inputContainer)
ViewUtils.hideView(binding.showFpsText) ViewUtils.hideView(binding.fpsIndicatorView)
ViewUtils.hideView(binding.thermalIndicatorView)
ViewUtils.hideView(binding.ramMeterView)
} }
} }
emulationViewModel.drawerOpen.collect(viewLifecycleOwner) { emulationViewModel.drawerOpen.collect(viewLifecycleOwner) {
@@ -486,22 +493,16 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
private fun updateShowFpsOverlay() { private fun updateShowFpsOverlay() {
val showOverlay = BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean() val showOverlay = BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean()
binding.showFpsText.setVisible(showOverlay) binding.fpsIndicatorView.setVisible(showOverlay)
if (showOverlay) { if (showOverlay) {
val SYSTEM_FPS = 0
val FPS = 1 val FPS = 1
val FRAMETIME = 2
val SPEED = 3
perfStatsUpdater = { perfStatsUpdater = {
if (emulationViewModel.emulationStarted.value && if (emulationViewModel.emulationStarted.value &&
!emulationViewModel.isEmulationStopping.value !emulationViewModel.isEmulationStopping.value
) { ) {
val perfStats = NativeLibrary.getPerfStats() val perfStats = NativeLibrary.getPerfStats()
val cpuBackend = NativeLibrary.getCpuBackend()
val gpuDriver = NativeLibrary.getGpuDriver()
if (_binding != null) { if (_binding != null) {
binding.showFpsText.text = binding.fpsIndicatorView.updateFps(perfStats[FPS].toFloat())
String.format("FPS: %.1f\n%s/%s", perfStats[FPS], cpuBackend, gpuDriver)
} }
perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 800) perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 800)
} }
@@ -516,26 +517,17 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
private fun updateThermalOverlay() { private fun updateThermalOverlay() {
val showOverlay = BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean() val showOverlay = BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean()
binding.showThermalsText.setVisible(showOverlay) binding.thermalIndicatorView.setVisible(showOverlay)
if (showOverlay) { if (showOverlay) {
thermalStatsUpdater = { thermalStatsUpdater = {
if (emulationViewModel.emulationStarted.value && if (emulationViewModel.emulationStarted.value &&
!emulationViewModel.isEmulationStopping.value !emulationViewModel.isEmulationStopping.value
) { ) {
val thermalStatus = when (powerManager.currentThermalStatus) {
PowerManager.THERMAL_STATUS_LIGHT -> "😥"
PowerManager.THERMAL_STATUS_MODERATE -> "🥵"
PowerManager.THERMAL_STATUS_SEVERE -> "🔥"
PowerManager.THERMAL_STATUS_CRITICAL,
PowerManager.THERMAL_STATUS_EMERGENCY,
PowerManager.THERMAL_STATUS_SHUTDOWN -> "☢️"
else -> "🙂"
}
if (_binding != null) { if (_binding != null) {
binding.showThermalsText.text = thermalStatus val temperature = getBatteryTemperature(requireContext())
binding.thermalIndicatorView.updateTemperature(temperature)
} }
thermalStatsUpdateHandler.postDelayed(thermalStatsUpdater!!, 1000) thermalStatsUpdateHandler.postDelayed(thermalStatsUpdater!!, 2000)
} }
} }
thermalStatsUpdateHandler.post(thermalStatsUpdater!!) thermalStatsUpdateHandler.post(thermalStatsUpdater!!)
@@ -546,6 +538,42 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
} }
private fun updateRamMeterOverlay() {
val showOverlay = BooleanSetting.SHOW_RAM_METER.getBoolean()
binding.ramMeterView.setVisible(showOverlay)
if (showOverlay) {
ramStatsUpdater = {
if (emulationViewModel.emulationStarted.value &&
!emulationViewModel.isEmulationStopping.value
) {
if (_binding != null) {
binding.ramMeterView.updateRamUsage()
}
ramStatsUpdateHandler.postDelayed(ramStatsUpdater!!, 1500)
}
}
ramStatsUpdateHandler.post(ramStatsUpdater!!)
} else {
if (ramStatsUpdater != null) {
ramStatsUpdateHandler.removeCallbacks(ramStatsUpdater!!)
}
}
}
private fun getBatteryTemperature(context: Context): Float {
return try {
val batteryIntent = context.registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
if (batteryIntent != null) {
val temperature = batteryIntent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 250)
temperature / 10f // Convert from tenths of degrees to degrees
} else {
25f // Fallback temperature
}
} catch (e: Exception) {
25f // Fallback temperature
}
}
@SuppressLint("SourceLockedOrientationActivity") @SuppressLint("SourceLockedOrientationActivity")
private fun updateOrientation() { private fun updateOrientation() {
emulationActivity?.let { emulationActivity?.let {
@@ -676,6 +704,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean() BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean()
findItem(R.id.thermal_indicator).isChecked = findItem(R.id.thermal_indicator).isChecked =
BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean() BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean()
findItem(R.id.ram_meter).isChecked =
BooleanSetting.SHOW_RAM_METER.getBoolean()
findItem(R.id.menu_rel_stick_center).isChecked = findItem(R.id.menu_rel_stick_center).isChecked =
BooleanSetting.JOYSTICK_REL_CENTER.getBoolean() BooleanSetting.JOYSTICK_REL_CENTER.getBoolean()
findItem(R.id.menu_dpad_slide).isChecked = BooleanSetting.DPAD_SLIDE.getBoolean() findItem(R.id.menu_dpad_slide).isChecked = BooleanSetting.DPAD_SLIDE.getBoolean()
@@ -702,6 +732,13 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
true true
} }
R.id.ram_meter -> {
it.isChecked = !it.isChecked
BooleanSetting.SHOW_RAM_METER.setBoolean(it.isChecked)
updateRamMeterOverlay()
true
}
R.id.menu_edit_overlay -> { R.id.menu_edit_overlay -> {
binding.drawerLayout.close() binding.drawerLayout.close()
binding.surfaceInputOverlay.requestFocus() binding.surfaceInputOverlay.requestFocus()
@@ -1046,5 +1083,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
companion object { companion object {
private val perfStatsUpdateHandler = Handler(Looper.myLooper()!!) private val perfStatsUpdateHandler = Handler(Looper.myLooper()!!)
private val thermalStatsUpdateHandler = Handler(Looper.myLooper()!!) private val thermalStatsUpdateHandler = Handler(Looper.myLooper()!!)
private val ramStatsUpdateHandler = Handler(Looper.myLooper()!!)
} }
} }

View File

@@ -0,0 +1,141 @@
// SPDX-FileCopyrightText: 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.citron.citron_emu.views
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.RectF
import android.graphics.Typeface
import android.util.AttributeSet
import android.util.Log
import android.view.View
import kotlin.math.roundToInt
class FpsIndicatorView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
private val backgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.parseColor("#80000000") // Semi-transparent black
style = Paint.Style.FILL
}
private val borderPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.WHITE
style = Paint.Style.STROKE
strokeWidth = 2f
}
private val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.WHITE
textSize = 22f
typeface = Typeface.DEFAULT_BOLD
textAlign = Paint.Align.CENTER
setShadowLayer(2f, 1f, 1f, Color.BLACK)
}
private val smallTextPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.WHITE
textSize = 16f
typeface = Typeface.DEFAULT
textAlign = Paint.Align.CENTER
setShadowLayer(2f, 1f, 1f, Color.BLACK)
}
private val iconPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.WHITE
textSize = 24f
textAlign = Paint.Align.CENTER
setShadowLayer(2f, 1f, 1f, Color.BLACK)
}
private var currentFps: Float = 0f
private val fpsIcon: String = "📊"
private val backgroundRect = RectF()
fun updateFps(fps: Float) {
try {
currentFps = fps
Log.d("FpsIndicator", "FPS updated: $currentFps")
// Update color based on FPS performance
val fpsColor = when {
currentFps >= 55f -> Color.parseColor("#4CAF50") // Green - Good performance
currentFps >= 45f -> Color.parseColor("#FF9800") // Orange - Moderate performance
currentFps >= 30f -> Color.parseColor("#FF5722") // Red orange - Poor performance
else -> Color.parseColor("#F44336") // Red - Very poor performance
}
textPaint.color = fpsColor
smallTextPaint.color = fpsColor
borderPaint.color = fpsColor
// Always invalidate to trigger a redraw
invalidate()
Log.d("FpsIndicator", "View invalidated, FPS: $currentFps")
} catch (e: Exception) {
// Fallback in case of any errors
currentFps = 0f
Log.e("FpsIndicator", "Error updating FPS", e)
invalidate()
}
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val desiredWidth = 120
val desiredHeight = 60
val widthMode = MeasureSpec.getMode(widthMeasureSpec)
val widthSize = MeasureSpec.getSize(widthMeasureSpec)
val heightMode = MeasureSpec.getMode(heightMeasureSpec)
val heightSize = MeasureSpec.getSize(heightMeasureSpec)
val width = when (widthMode) {
MeasureSpec.EXACTLY -> widthSize
MeasureSpec.AT_MOST -> minOf(desiredWidth, widthSize)
else -> desiredWidth
}
val height = when (heightMode) {
MeasureSpec.EXACTLY -> heightSize
MeasureSpec.AT_MOST -> minOf(desiredHeight, heightSize)
else -> desiredHeight
}
setMeasuredDimension(width, height)
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
backgroundRect.set(4f, 4f, w - 4f, h - 4f)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// Draw background with rounded corners
canvas.drawRoundRect(backgroundRect, 12f, 12f, backgroundPaint)
canvas.drawRoundRect(backgroundRect, 12f, 12f, borderPaint)
val centerX = width / 2f
val centerY = height / 2f
// Draw FPS icon on the left
canvas.drawText(fpsIcon, 18f, centerY - 8f, iconPaint)
// Draw FPS value (main text)
val fpsText = "${currentFps.roundToInt()}"
canvas.drawText(fpsText, centerX, centerY - 8f, textPaint)
// Draw "FPS" label (smaller text below)
canvas.drawText("FPS", centerX, centerY + 12f, smallTextPaint)
Log.d("FpsIndicator", "onDraw called - FPS: $fpsText")
}
}

View File

@@ -0,0 +1,195 @@
// SPDX-FileCopyrightText: 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.citron.citron_emu.views
import android.app.ActivityManager
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.RectF
import android.graphics.Typeface
import android.util.AttributeSet
import android.util.Log
import android.view.View
import kotlin.math.roundToInt
class RamMeterView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
private val backgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.parseColor("#80000000") // Semi-transparent black
style = Paint.Style.FILL
}
private val borderPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.WHITE
style = Paint.Style.STROKE
strokeWidth = 2f
}
private val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.WHITE
textSize = 20f
typeface = Typeface.DEFAULT_BOLD
textAlign = Paint.Align.CENTER
setShadowLayer(2f, 1f, 1f, Color.BLACK)
}
private val smallTextPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.WHITE
textSize = 14f
typeface = Typeface.DEFAULT
textAlign = Paint.Align.CENTER
setShadowLayer(2f, 1f, 1f, Color.BLACK)
}
private val iconPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.WHITE
textSize = 24f
textAlign = Paint.Align.CENTER
setShadowLayer(2f, 1f, 1f, Color.BLACK)
}
private val meterBackgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.parseColor("#40FFFFFF") // Semi-transparent white
style = Paint.Style.FILL
}
private val meterFillPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.GREEN
style = Paint.Style.FILL
}
private var ramUsagePercent: Float = 0f
private var usedRamMB: Long = 0L
private var totalRamMB: Long = 0L
private var ramIcon: String = "🧠"
private val backgroundRect = RectF()
private val meterBackgroundRect = RectF()
private val meterFillRect = RectF()
fun updateRamUsage() {
try {
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val memoryInfo = ActivityManager.MemoryInfo()
activityManager.getMemoryInfo(memoryInfo)
totalRamMB = memoryInfo.totalMem / (1024 * 1024)
val availableRamMB = memoryInfo.availMem / (1024 * 1024)
usedRamMB = totalRamMB - availableRamMB
ramUsagePercent = (usedRamMB.toFloat() / totalRamMB.toFloat()) * 100f
// Update meter color based on usage
val meterColor = when {
ramUsagePercent < 50f -> Color.parseColor("#4CAF50") // Green
ramUsagePercent < 75f -> Color.parseColor("#FF9800") // Orange
ramUsagePercent < 90f -> Color.parseColor("#FF5722") // Red orange
else -> Color.parseColor("#F44336") // Red
}
meterFillPaint.color = meterColor
textPaint.color = meterColor
smallTextPaint.color = meterColor
borderPaint.color = meterColor
// Update icon based on usage
ramIcon = when {
ramUsagePercent < 50f -> "🧠" // Normal brain
ramUsagePercent < 75f -> "" // Warning
ramUsagePercent < 90f -> "🔥" // Hot
else -> "💥" // Critical
}
invalidate()
Log.d("RamMeter", "RAM usage updated: ${ramUsagePercent.roundToInt()}% (${usedRamMB}MB/${totalRamMB}MB)")
} catch (e: Exception) {
Log.e("RamMeter", "Error updating RAM usage", e)
ramUsagePercent = 0f
usedRamMB = 0L
totalRamMB = 0L
invalidate()
}
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val desiredWidth = 140
val desiredHeight = 60
val widthMode = MeasureSpec.getMode(widthMeasureSpec)
val widthSize = MeasureSpec.getSize(widthMeasureSpec)
val heightMode = MeasureSpec.getMode(heightMeasureSpec)
val heightSize = MeasureSpec.getSize(heightMeasureSpec)
val width = when (widthMode) {
MeasureSpec.EXACTLY -> widthSize
MeasureSpec.AT_MOST -> minOf(desiredWidth, widthSize)
else -> desiredWidth
}
val height = when (heightMode) {
MeasureSpec.EXACTLY -> heightSize
MeasureSpec.AT_MOST -> minOf(desiredHeight, heightSize)
else -> desiredHeight
}
setMeasuredDimension(width, height)
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
backgroundRect.set(4f, 4f, w - 4f, h - 4f)
// Setup meter rectangles - compact horizontal bar at the bottom
val meterLeft = 30f
val meterTop = h - 18f
val meterRight = w - 10f
val meterBottom = h - 10f
meterBackgroundRect.set(meterLeft, meterTop, meterRight, meterBottom)
meterFillRect.set(meterLeft, meterTop, meterRight, meterBottom)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// Draw background with rounded corners
canvas.drawRoundRect(backgroundRect, 12f, 12f, backgroundPaint)
canvas.drawRoundRect(backgroundRect, 12f, 12f, borderPaint)
val centerX = width / 2f
val centerY = height / 2f
// Draw RAM icon on the left
canvas.drawText(ramIcon, 18f, centerY - 8f, iconPaint)
// Draw percentage text at the top center
val percentText = "${ramUsagePercent.roundToInt()}%"
canvas.drawText(percentText, centerX, centerY - 8f, textPaint)
// Draw memory usage text below percentage
val usedGB = usedRamMB / 1024f
val totalGB = totalRamMB / 1024f
val memoryText = if (totalGB >= 1.0f) {
"%.1fGB/%.1fGB".format(usedGB, totalGB)
} else {
"${usedRamMB}MB/${totalRamMB}MB"
}
canvas.drawText(memoryText, centerX, centerY + 8f, smallTextPaint)
// Draw RAM meter background at the bottom
canvas.drawRoundRect(meterBackgroundRect, 4f, 4f, meterBackgroundPaint)
// Draw RAM meter fill
val fillWidth = meterBackgroundRect.width() * (ramUsagePercent / 100f)
meterFillRect.right = meterBackgroundRect.left + fillWidth
canvas.drawRoundRect(meterFillRect, 4f, 4f, meterFillPaint)
Log.d("RamMeter", "onDraw called - Usage: $percentText, Memory: $memoryText")
}
}

View File

@@ -0,0 +1,159 @@
// SPDX-FileCopyrightText: 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.citron.citron_emu.views
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.RectF
import android.graphics.Typeface
import android.os.BatteryManager
import android.util.AttributeSet
import android.util.Log
import android.view.View
import androidx.core.content.ContextCompat
import org.citron.citron_emu.R
import kotlin.math.roundToInt
class ThermalIndicatorView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
private val backgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.parseColor("#80000000") // Semi-transparent black
style = Paint.Style.FILL
}
private val borderPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.WHITE
style = Paint.Style.STROKE
strokeWidth = 2f
}
private val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.WHITE
textSize = 22f
typeface = Typeface.DEFAULT_BOLD
textAlign = Paint.Align.CENTER
setShadowLayer(2f, 1f, 1f, Color.BLACK)
}
private val smallTextPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.WHITE
textSize = 16f
typeface = Typeface.DEFAULT
textAlign = Paint.Align.CENTER
setShadowLayer(2f, 1f, 1f, Color.BLACK)
}
private val iconPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.WHITE
textSize = 24f
textAlign = Paint.Align.CENTER
setShadowLayer(2f, 1f, 1f, Color.BLACK)
}
private var batteryTemperature: Float = 0f
private var thermalStatus: String = "🌡️"
private val backgroundRect = RectF()
fun updateTemperature(temperature: Float) {
try {
batteryTemperature = temperature
Log.d("ThermalIndicator", "Battery temperature updated: ${batteryTemperature}°C")
// Update thermal status icon based on temperature
thermalStatus = when {
batteryTemperature < 20f -> "❄️" // Cold
batteryTemperature < 30f -> "🌡️" // Normal
batteryTemperature < 40f -> "🔥" // Warm
batteryTemperature < 50f -> "🥵" // Hot
else -> "☢️" // Critical
}
// Update text color based on temperature
val tempColor = when {
batteryTemperature < 20f -> Color.parseColor("#87CEEB") // Sky blue
batteryTemperature < 30f -> Color.WHITE // White
batteryTemperature < 40f -> Color.parseColor("#FFA500") // Orange
batteryTemperature < 50f -> Color.parseColor("#FF4500") // Red orange
else -> Color.parseColor("#FF0000") // Red
}
textPaint.color = tempColor
smallTextPaint.color = tempColor
borderPaint.color = tempColor
// Always invalidate to trigger a redraw
invalidate()
Log.d("ThermalIndicator", "View invalidated, temperature: ${batteryTemperature}°C, status: $thermalStatus")
} catch (e: Exception) {
// Fallback in case of any errors
batteryTemperature = 25f
thermalStatus = "🌡️"
Log.e("ThermalIndicator", "Error updating temperature", e)
invalidate()
}
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val desiredWidth = 120
val desiredHeight = 60
val widthMode = MeasureSpec.getMode(widthMeasureSpec)
val widthSize = MeasureSpec.getSize(widthMeasureSpec)
val heightMode = MeasureSpec.getMode(heightMeasureSpec)
val heightSize = MeasureSpec.getSize(heightMeasureSpec)
val width = when (widthMode) {
MeasureSpec.EXACTLY -> widthSize
MeasureSpec.AT_MOST -> minOf(desiredWidth, widthSize)
else -> desiredWidth
}
val height = when (heightMode) {
MeasureSpec.EXACTLY -> heightSize
MeasureSpec.AT_MOST -> minOf(desiredHeight, heightSize)
else -> desiredHeight
}
setMeasuredDimension(width, height)
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
backgroundRect.set(4f, 4f, w - 4f, h - 4f)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// Draw background with rounded corners
canvas.drawRoundRect(backgroundRect, 12f, 12f, backgroundPaint)
canvas.drawRoundRect(backgroundRect, 12f, 12f, borderPaint)
val centerX = width / 2f
val centerY = height / 2f
// Draw thermal icon on the left
canvas.drawText(thermalStatus, 18f, centerY - 8f, iconPaint)
// Draw temperature in Celsius (main temperature)
val celsiusText = "${batteryTemperature.roundToInt()}°C"
canvas.drawText(celsiusText, centerX, centerY - 8f, textPaint)
// Draw temperature in Fahrenheit (smaller, below)
val fahrenheit = (batteryTemperature * 9f / 5f + 32f).roundToInt()
val fahrenheitText = "${fahrenheit}°F"
canvas.drawText(fahrenheitText, centerX, centerY + 12f, smallTextPaint)
Log.d("ThermalIndicator", "onDraw called - Celsius: $celsiusText, Fahrenheit: $fahrenheitText")
}
}

View File

@@ -1,4 +1,5 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project // SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-FileCopyrightText: 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
@@ -69,6 +70,8 @@ struct Values {
Settings::Category::Overlay}; Settings::Category::Overlay};
Settings::Setting<bool> show_thermal_overlay{linkage, false, "show_thermal_overlay", Settings::Setting<bool> show_thermal_overlay{linkage, false, "show_thermal_overlay",
Settings::Category::Overlay}; Settings::Category::Overlay};
Settings::Setting<bool> show_ram_meter{linkage, false, "show_ram_meter",
Settings::Category::Overlay};
Settings::Setting<bool> show_input_overlay{linkage, true, "show_input_overlay", Settings::Setting<bool> show_input_overlay{linkage, true, "show_input_overlay",
Settings::Category::Overlay}; Settings::Category::Overlay};
Settings::Setting<bool> touchscreen{linkage, true, "touchscreen", Settings::Category::Overlay}; Settings::Setting<bool> touchscreen{linkage, true, "touchscreen", Settings::Category::Overlay};

View File

@@ -143,31 +143,41 @@
android:layout_marginHorizontal="20dp" android:layout_marginHorizontal="20dp"
android:fitsSystemWindows="true"> android:fitsSystemWindows="true">
<com.google.android.material.textview.MaterialTextView <LinearLayout
android:id="@+id/show_fps_text"
style="@style/TextAppearance.Material3.BodySmall"
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="center_horizontal|top"
android:clickable="false" android:layout_marginTop="16dp"
android:focusable="false" android:orientation="horizontal"
android:textColor="@android:color/white" android:gravity="center">
android:shadowColor="@android:color/black"
android:shadowRadius="3"
tools:ignore="RtlHardcoded" />
<com.google.android.material.textview.MaterialTextView <org.citron.citron_emu.views.FpsIndicatorView
android:id="@+id/show_thermals_text" android:id="@+id/fps_indicator_view"
style="@style/TextAppearance.Material3.BodySmall" android:layout_width="wrap_content"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_height="wrap_content" android:layout_marginEnd="8dp"
android:layout_gravity="right" android:clickable="false"
android:clickable="false" android:focusable="false"
android:focusable="false" android:visibility="gone" />
android:textColor="@android:color/white"
android:shadowColor="@android:color/black" <org.citron.citron_emu.views.ThermalIndicatorView
android:shadowRadius="3" android:id="@+id/thermal_indicator_view"
tools:ignore="RtlHardcoded" /> android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:clickable="false"
android:focusable="false"
android:visibility="gone" />
<org.citron.citron_emu.views.RamMeterView
android:id="@+id/ram_meter_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="false"
android:focusable="false"
android:visibility="gone" />
</LinearLayout>
</FrameLayout> </FrameLayout>

View File

@@ -11,6 +11,11 @@
android:title="@string/emulation_thermal_indicator" android:title="@string/emulation_thermal_indicator"
android:checkable="true" /> android:checkable="true" />
<item
android:id="@+id/ram_meter"
android:title="@string/emulation_ram_meter"
android:checkable="true" />
<item <item
android:id="@+id/menu_edit_overlay" android:id="@+id/menu_edit_overlay"
android:title="@string/emulation_touch_overlay_edit" /> android:title="@string/emulation_touch_overlay_edit" />

View File

@@ -303,4 +303,71 @@
<item>2</item> <item>2</item>
</integer-array> </integer-array>
<!-- Zep Zone setting arrays -->
<string-array name="memoryLayoutNames">
<item>4GB DRAM (Default)</item>
<item>6GB DRAM (Unsafe)</item>
<item>8GB DRAM (Unsafe)</item>
<item>10GB DRAM (Unsafe)</item>
<item>12GB DRAM (Unsafe)</item>
<item>14GB DRAM (Unsafe)</item>
<item>16GB DRAM (Unsafe)</item>
</string-array>
<integer-array name="memoryLayoutValues">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
<item>5</item>
<item>6</item>
</integer-array>
<string-array name="astcDecodeModeNames">
<item>CPU</item>
<item>GPU</item>
<item>CPU Asynchronous</item>
</string-array>
<integer-array name="astcDecodeModeValues">
<item>0</item>
<item>1</item>
<item>2</item>
</integer-array>
<string-array name="astcRecompressionNames">
<item>Uncompressed</item>
<item>BC1</item>
<item>BC3</item>
</string-array>
<integer-array name="astcRecompressionValues">
<item>0</item>
<item>1</item>
<item>2</item>
</integer-array>
<string-array name="shaderBackendNames">
<item>GLSL</item>
<item>GLASM</item>
<item>SPIR-V</item>
</string-array>
<integer-array name="shaderBackendValues">
<item>0</item>
<item>1</item>
<item>2</item>
</integer-array>
<string-array name="vramUsageModeNames">
<item>Conservative</item>
<item>Aggressive</item>
</string-array>
<integer-array name="vramUsageModeValues">
<item>0</item>
<item>1</item>
</integer-array>
</resources> </resources>

View File

@@ -410,6 +410,13 @@
<string name="preferences_theme">Theme and color</string> <string name="preferences_theme">Theme and color</string>
<string name="preferences_debug">Debug</string> <string name="preferences_debug">Debug</string>
<string name="preferences_debug_description">CPU/GPU debugging, graphics API, fastmem</string> <string name="preferences_debug_description">CPU/GPU debugging, graphics API, fastmem</string>
<string name="preferences_zep_zone">Zep Zone</string>
<string name="preferences_zep_zone_description">Advanced emulation settings</string>
<!-- Zep Zone Headers -->
<string name="memory_layout_header">Memory Layout</string>
<string name="astc_settings_header">ASTC Settings</string>
<string name="advanced_graphics_header">Advanced Graphics</string>
<!-- Game properties --> <!-- Game properties -->
<string name="info">Info</string> <string name="info">Info</string>
@@ -472,7 +479,8 @@
<string name="emulation_exit">Exit emulation</string> <string name="emulation_exit">Exit emulation</string>
<string name="emulation_done">Done</string> <string name="emulation_done">Done</string>
<string name="emulation_fps_counter">FPS counter</string> <string name="emulation_fps_counter">FPS counter</string>
<string name="emulation_thermal_indicator">Thermal indicator</string> <string name="emulation_thermal_indicator">Battery temperature</string>
<string name="emulation_ram_meter">RAM usage meter</string>
<string name="emulation_toggle_controls">Toggle controls</string> <string name="emulation_toggle_controls">Toggle controls</string>
<string name="emulation_rel_stick_center">Relative stick center</string> <string name="emulation_rel_stick_center">Relative stick center</string>
<string name="emulation_dpad_slide">D-pad slide</string> <string name="emulation_dpad_slide">D-pad slide</string>
@@ -1167,4 +1175,16 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
</string> </string>
<!-- Zep Zone settings strings -->
<string name="memory_layout_mode">Memory Layout</string>
<string name="memory_layout_mode_description">Increases the amount of emulated RAM from the stock 4GB of the retail Switch to the developer kit\'s 8/6GB. It doesn\'t improve stability or performance and is intended to let big texture mods fit in emulated RAM. Enabling it will increase memory use. It is not recommended to enable unless a specific game with a texture mod needs it.</string>
<string name="astc_decode_mode">ASTC Decoding Method</string>
<string name="astc_decode_mode_description">Controls how ASTC textures are decoded. GPU decoding is faster but may cause issues on some devices.</string>
<string name="astc_recompression">ASTC Recompression Method</string>
<string name="astc_recompression_description">Controls how ASTC textures are recompressed when GPU doesn\'t support them natively.</string>
<string name="shader_backend">Shader Backend</string>
<string name="shader_backend_description">Controls which shader backend to use for rendering.</string>
<string name="vram_usage_mode">VRAM Usage Mode</string>
<string name="vram_usage_mode_description">Controls how aggressively VRAM is used. Conservative mode limits VRAM usage for better stability.</string>
</resources> </resources>

View File

@@ -9,8 +9,8 @@
namespace Service::Sockets { namespace Service::Sockets {
constexpr Result ResultNotImplemented{ErrorModule::NSD, 1}; // Generic "not implemented" [[maybe_unused]] constexpr Result ResultNotImplemented{ErrorModule::NSD, 1}; // Generic "not implemented"
constexpr Result ResultNsdNotInitialized{ErrorModule::NSD, 2}; // Example, if needed [[maybe_unused]] constexpr Result ResultNsdNotInitialized{ErrorModule::NSD, 2}; // Example, if needed
constexpr Result ResultPermissionDenied{ErrorModule::NSD, 3}; // For nsd:a specific calls constexpr Result ResultPermissionDenied{ErrorModule::NSD, 3}; // For nsd:a specific calls
constexpr Result ResultOverflow{ErrorModule::NSD, 6}; constexpr Result ResultOverflow{ErrorModule::NSD, 6};