Merge remote-tracking branch 'origin/master' into warmenhoven/dev/azahar_libretro

This commit is contained in:
Eric Warmenhoven 2025-09-16 12:45:33 -04:00
commit eb6295354d
No known key found for this signature in database
33 changed files with 447 additions and 171 deletions

View File

@ -318,7 +318,7 @@ find_package(Threads REQUIRED)
if (ENABLE_QT)
if (NOT USE_SYSTEM_QT)
download_qt(6.7.2)
download_qt(6.9.2)
endif()
find_package(Qt6 REQUIRED COMPONENTS Widgets Multimedia Concurrent)

View File

@ -20,9 +20,9 @@ function(determine_qt_parameters target host_out type_out arch_out arch_path_out
set(arch_path "mingw_64")
elseif (MSVC)
if ("arm64" IN_LIST ARCHITECTURE)
set(arch_path "msvc2019_arm64")
set(arch_path "msvc2022_arm64")
elseif ("x86_64" IN_LIST ARCHITECTURE)
set(arch_path "msvc2019_64")
set(arch_path "msvc2022_64")
else()
message(FATAL_ERROR "Unsupported bundled Qt architecture. Enable USE_SYSTEM_QT and provide your own.")
endif()
@ -30,12 +30,13 @@ function(determine_qt_parameters target host_out type_out arch_out arch_path_out
# In case we're cross-compiling, prepare to also fetch the correct host Qt tools.
if (CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "AMD64")
set(host_arch_path "msvc2019_64")
set(host_arch_path "msvc2022_64")
elseif (CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "ARM64")
# TODO: msvc2019_arm64 doesn't include some of the required tools for some reason,
# TODO: so until it does, just use msvc2019_64 under x86_64 emulation.
# TODO: ^ Is this still true with msvc2022?
# set(host_arch_path "msvc2019_arm64")
set(host_arch_path "msvc2019_64")
set(host_arch_path "msvc2022_64")
endif()
set(host_arch "win64_${host_arch_path}")
else()
@ -105,7 +106,7 @@ function(download_qt_configuration prefix_out target host type arch arch_path ba
if (NOT EXISTS "${prefix}")
message(STATUS "Downloading Qt binaries for ${target}:${host}:${type}:${arch}:${arch_path}")
set(AQT_PREBUILD_BASE_URL "https://github.com/miurahr/aqtinstall/releases/download/v3.1.18")
set(AQT_PREBUILD_BASE_URL "https://github.com/miurahr/aqtinstall/releases/download/v3.3.0")
if (WIN32)
set(aqt_path "${base_path}/aqt.exe")
if (NOT EXISTS "${aqt_path}")

View File

@ -75,6 +75,9 @@
<true/>
<key>NSHighResolutionCapable</key>
<string>True</string>
<key>UIDesignRequiresCompatibility</key>
<true/> <!-- Remove when Qt Liquid Glass issues are fixed upstream:
https://bugreports.qt.io/browse/QTBUG-138942 -->
<key>UIFileSharingEnabled</key>
<true/>
<key>UILaunchStoryboardName</key>

@ -1 +1 @@
Subproject commit 4f39041699412873d0afcec89a9313148a192647
Subproject commit a36decbe43d0e5a570ac3d3ba9a0b226dc832a17

View File

@ -64,7 +64,7 @@ target_link_libraries(catch2 INTERFACE Catch2::Catch2WithMain)
if(USE_SYSTEM_CRYPTOPP)
find_package(cryptopp REQUIRED)
add_library(cryptopp INTERFACE)
target_link_libraries(cryptopp SYSTEM INTERFACE cryptopp::cryptopp)
target_link_libraries(cryptopp INTERFACE cryptopp::cryptopp)
else()
if (WIN32 AND NOT MSVC AND "arm64" IN_LIST ARCHITECTURE)
# TODO: CryptoPP ARM64 ASM does not seem to support Windows unless compiled with MSVC.
@ -89,7 +89,7 @@ target_disable_warnings(dds-ktx)
if(USE_SYSTEM_FMT)
add_library(fmt INTERFACE)
find_package(fmt REQUIRED)
target_link_libraries(fmt SYSTEM INTERFACE fmt::fmt)
target_link_libraries(fmt INTERFACE fmt::fmt)
else()
option(FMT_INSTALL "" ON)
add_subdirectory(fmt EXCLUDE_FROM_ALL)
@ -101,7 +101,7 @@ if ("x86_64" IN_LIST ARCHITECTURE)
if(USE_SYSTEM_XBYAK)
find_package(xbyak REQUIRED)
add_library(xbyak INTERFACE)
target_link_libraries(xbyak SYSTEM INTERFACE xbyak::xbyak)
target_link_libraries(xbyak INTERFACE xbyak::xbyak)
else()
add_subdirectory(xbyak EXCLUDE_FROM_ALL)
endif()
@ -117,7 +117,7 @@ if ("x86_64" IN_LIST ARCHITECTURE OR "arm64" IN_LIST ARCHITECTURE)
if(USE_SYSTEM_DYNARMIC)
find_package(dynarmic REQUIRED)
add_library(dynarmic INTERFACE)
target_link_libraries(dynarmic SYSTEM INTERFACE dynarmic::dynarmic)
target_link_libraries(dynarmic INTERFACE dynarmic::dynarmic)
# The dynarmic package's cmake files are helpfully completely silent
# so we have to inform the user of its status ourselves
if(TARGET dynarmic::dynarmic)
@ -140,7 +140,7 @@ endif()
if(USE_SYSTEM_INIH)
find_package(inih REQUIRED COMPONENTS inih inir)
add_library(inih INTERFACE)
target_link_libraries(inih SYSTEM INTERFACE inih::inih inih::inir)
target_link_libraries(inih INTERFACE inih::inih inih::inir)
else()
add_subdirectory(inih)
endif()
@ -221,7 +221,7 @@ if(USE_SYSTEM_ZSTD)
if(TARGET zstd::libzstd_shared)
message(STATUS "Found system Zstandard")
endif()
target_link_libraries(zstd SYSTEM INTERFACE zstd::libzstd_shared)
target_link_libraries(zstd INTERFACE zstd::libzstd_shared)
else()
set(ZSTD_LEGACY_SUPPORT OFF)
set(ZSTD_BUILD_PROGRAMS OFF)
@ -256,7 +256,7 @@ endif()
if(USE_SYSTEM_ENET)
find_package(libenet REQUIRED)
add_library(enet INTERFACE)
target_link_libraries(enet SYSTEM INTERFACE libenet::libenet)
target_link_libraries(enet INTERFACE libenet::libenet)
else()
add_subdirectory(enet)
target_include_directories(enet INTERFACE ./enet/include)
@ -267,7 +267,7 @@ if (ENABLE_CUBEB)
if(USE_SYSTEM_CUBEB)
find_package(cubeb REQUIRED)
add_library(cubeb INTERFACE)
target_link_libraries(cubeb SYSTEM INTERFACE cubeb::cubeb)
target_link_libraries(cubeb INTERFACE cubeb::cubeb)
if(TARGET cubeb::cubeb)
message(STATUS "Found system cubeb")
endif()
@ -367,7 +367,7 @@ if(USE_SYSTEM_CPP_HTTPLIB)
target_disable_warnings(httplib)
else()
if(CppHttp_FOUND)
target_link_libraries(httplib SYSTEM INTERFACE httplib::httplib)
target_link_libraries(httplib INTERFACE httplib::httplib)
else()
message(STATUS "Cpp-httplib not found or not suitable version! Falling back to bundled...")
target_include_directories(httplib INTERFACE ./httplib)
@ -390,7 +390,7 @@ if (ENABLE_WEB_SERVICE)
if (USE_SYSTEM_CPP_JWT)
find_package(cpp-jwt REQUIRED)
add_library(cpp-jwt INTERFACE)
target_link_libraries(cpp-jwt SYSTEM INTERFACE cpp-jwt::cpp-jwt)
target_link_libraries(cpp-jwt INTERFACE cpp-jwt::cpp-jwt)
else()
add_library(cpp-jwt INTERFACE)
target_include_directories(cpp-jwt INTERFACE ./cpp-jwt/include)
@ -403,7 +403,7 @@ endif()
if(USE_SYSTEM_LODEPNG)
add_library(lodepng INTERFACE)
find_package(lodepng REQUIRED)
target_link_libraries(lodepng SYSTEM INTERFACE lodepng::lodepng)
target_link_libraries(lodepng INTERFACE lodepng::lodepng)
else()
add_subdirectory(lodepng)
endif()
@ -420,7 +420,7 @@ if (ENABLE_OPENAL)
if(USE_SYSTEM_OPENAL)
add_library(OpenAL INTERFACE)
find_package(OpenAL REQUIRED)
target_link_libraries(OpenAL SYSTEM INTERFACE OpenAL::OpenAL)
target_link_libraries(OpenAL INTERFACE OpenAL::OpenAL)
else()
set(ALSOFT_EMBED_HRTF_DATA OFF CACHE BOOL "")
set(ALSOFT_EXAMPLES OFF CACHE BOOL "")
@ -489,7 +489,7 @@ if (ENABLE_VULKAN)
find_package(VulkanMemoryAllocator REQUIRED)
if(TARGET GPUOpen::VulkanMemoryAllocator)
message(STATUS "Found VulkanMemoryAllocator")
target_link_libraries(vma SYSTEM INTERFACE GPUOpen::VulkanMemoryAllocator)
target_link_libraries(vma INTERFACE GPUOpen::VulkanMemoryAllocator)
endif()
else()
add_library(vma INTERFACE)
@ -503,7 +503,7 @@ if (ENABLE_VULKAN)
find_package(Vulkan REQUIRED)
if(TARGET Vulkan::Headers)
message(STATUS "Found Vulkan headers")
target_link_libraries(vulkan-headers SYSTEM INTERFACE Vulkan::Headers)
target_link_libraries(vulkan-headers INTERFACE Vulkan::Headers)
endif()
else()
target_include_directories(vulkan-headers INTERFACE ./vulkan-headers/include)

View File

@ -64,7 +64,7 @@ android {
// The application ID refers to Lime3DS to allow for
// the Play Store listing, which was originally set up for Lime3DS, to still be used.
applicationId = "io.github.lime3ds.android"
minSdk = 28
minSdk = 29
targetSdk = 35
versionCode = autoVersion
versionName = getGitVersion()
@ -186,7 +186,7 @@ dependencies {
// Download Vulkan Validation Layers from the KhronosGroup GitHub.
val downloadVulkanValidationLayers = tasks.register<Download>("downloadVulkanValidationLayers") {
src("https://github.com/KhronosGroup/Vulkan-ValidationLayers/releases/download/vulkan-sdk-1.4.304.1/android-binaries-1.4.304.1.zip")
src("https://github.com/KhronosGroup/Vulkan-ValidationLayers/releases/download/vulkan-sdk-1.4.313.0/android-binaries-1.4.313.0.zip")
dest(file("${layout.buildDirectory.get().asFile.path}/tmp/Vulkan-ValidationLayers.zip"))
onlyIfModified(true)
}

View File

@ -60,7 +60,15 @@ class EmulationActivity : AppCompatActivity() {
private lateinit var binding: ActivityEmulationBinding
private lateinit var screenAdjustmentUtil: ScreenAdjustmentUtil
private lateinit var hotkeyUtility: HotkeyUtility
private lateinit var secondaryDisplay: SecondaryDisplay;
private lateinit var secondaryDisplay: SecondaryDisplay
private val onShutdown = Runnable {
if (intent.getBooleanExtra("launched_from_shortcut", false)) {
finishAffinity()
} else {
this.finish()
}
}
private val emulationFragment: EmulationFragment
get() {
@ -77,8 +85,8 @@ class EmulationActivity : AppCompatActivity() {
ThemeUtil.setTheme(this)
settingsViewModel.settings.loadSettings()
super.onCreate(savedInstanceState)
secondaryDisplay = SecondaryDisplay(this);
secondaryDisplay.updateDisplay();
secondaryDisplay = SecondaryDisplay(this)
secondaryDisplay.updateDisplay()
binding = ActivityEmulationBinding.inflate(layoutInflater)
screenAdjustmentUtil = ScreenAdjustmentUtil(this, windowManager, settingsViewModel.settings)
@ -101,13 +109,7 @@ class EmulationActivity : AppCompatActivity() {
windowManager.defaultDisplay.rotation
)
EmulationLifecycleUtil.addShutdownHook(hook = {
if (intent.getBooleanExtra("launched_from_shortcut", false)) {
finishAffinity()
} else {
this.finish()
}
})
EmulationLifecycleUtil.addShutdownHook(onShutdown)
isEmulationRunning = true
instance = this
@ -165,12 +167,12 @@ class EmulationActivity : AppCompatActivity() {
}
override fun onDestroy() {
EmulationLifecycleUtil.clear()
EmulationLifecycleUtil.removeHook(onShutdown)
NativeLibrary.playTimeManagerStop()
isEmulationRunning = false
instance = null
secondaryDisplay.releasePresentation()
secondaryDisplay.releaseVD();
secondaryDisplay.releaseVD()
super.onDestroy()
}

View File

@ -23,15 +23,12 @@ class SecondaryDisplay(val context: Context) {
private val vd: VirtualDisplay
init {
val st = SurfaceTexture(0)
st.setDefaultBufferSize(1920, 1080)
val vdSurface = Surface(st)
vd = displayManager.createVirtualDisplay(
"HiddenDisplay",
1920,
1080,
320,
vdSurface,
null,
DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION
)
}

View File

@ -19,13 +19,14 @@ enum class BooleanSetting(
INSTANT_DEBUG_LOG("instant_debug_log", Settings.SECTION_DEBUG, false),
ENABLE_RPC_SERVER("enable_rpc_server", Settings.SECTION_DEBUG, 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),
PERF_OVERLAY_ENABLE("performance_overlay_enable", Settings.SECTION_LAYOUT, false),
PERF_OVERLAY_SHOW_FPS("performance_overlay_show_fps", Settings.SECTION_LAYOUT, true),
PERF_OVERLAY_SHOW_FRAMETIME("performance_overlay_show_frame_time", Settings.SECTION_LAYOUT, false),
PERF_OVERLAY_SHOW_SPEED("performance_overlay_show_speed", Settings.SECTION_LAYOUT, false),
PERF_OVERLAY_SHOW_APP_RAM_USAGE("performance_overlay_show_app_ram_usage", Settings.SECTION_LAYOUT, false),
PERF_OVERLAY_SHOW_AVAILABLE_RAM("performance_overlay_show_available_ram", Settings.SECTION_LAYOUT, false),
PERF_OVERLAY_SHOW_BATTERY_TEMP("performance_overlay_show_battery_temp", Settings.SECTION_LAYOUT, false),
PERF_OVERLAY_BACKGROUND("performance_overlay_background", Settings.SECTION_LAYOUT, false),
DELAY_START_LLE_MODULES("delay_start_for_lle_modules", Settings.SECTION_DEBUG, true),
DETERMINISTIC_ASYNC_OPERATIONS("deterministic_async_operations", Settings.SECTION_DEBUG, false),
REQUIRED_ONLINE_LLE_MODULES("enable_required_online_lle_modules", Settings.SECTION_SYSTEM, false),
@ -49,7 +50,8 @@ enum class BooleanSetting(
DISABLE_RIGHT_EYE_RENDER("disable_right_eye_render", Settings.SECTION_RENDERER, false),
USE_ARTIC_BASE_CONTROLLER("use_artic_base_controller", Settings.SECTION_CONTROLS, false),
UPRIGHT_SCREEN("upright_screen", Settings.SECTION_LAYOUT, false),
COMPRESS_INSTALLED_CIA_CONTENT("compress_cia_installs", Settings.SECTION_STORAGE, false);
COMPRESS_INSTALLED_CIA_CONTENT("compress_cia_installs", Settings.SECTION_STORAGE, false),
ANDROID_HIDE_IMAGES("android_hide_images", Settings.SECTION_CORE, false);
override var boolean: Boolean = defaultValue
@ -83,6 +85,8 @@ enum class BooleanSetting(
SHADERS_ACCURATE_MUL,
USE_ARTIC_BASE_CONTROLLER,
COMPRESS_INSTALLED_CIA_CONTENT,
ANDROID_HIDE_IMAGES,
PERF_OVERLAY_ENABLE // Works in overlay options, but not from the settings menu
)
fun from(key: String): BooleanSetting? =

View File

@ -1,4 +1,4 @@
// Copyright Citra Emulator Project / Lime3DS Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -10,6 +10,10 @@ enum class FloatSetting(
override val defaultValue: Float
) : AbstractFloatSetting {
LARGE_SCREEN_PROPORTION("large_screen_proportion",Settings.SECTION_LAYOUT,2.25f),
SECOND_SCREEN_OPACITY("custom_second_layer_opacity", Settings.SECTION_RENDERER, 100f),
BACKGROUND_RED("bg_red", Settings.SECTION_RENDERER, 0f),
BACKGROUND_BLUE("bg_blue", Settings.SECTION_RENDERER, 0f),
BACKGROUND_GREEN("bg_green", Settings.SECTION_RENDERER, 0f),
EMPTY_SETTING("", "", 0.0f);
override var float: Float = defaultValue

View File

@ -1,4 +1,4 @@
// Copyright Citra Emulator Project / Lime3DS Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -10,7 +10,6 @@ import org.citra.citra_emu.features.settings.model.AbstractSetting
import org.citra.citra_emu.features.settings.model.FloatSetting
import org.citra.citra_emu.features.settings.model.ScaledFloatSetting
import org.citra.citra_emu.utils.Log
import kotlin.math.roundToInt
class SliderSetting(
setting: AbstractSetting?,
@ -27,7 +26,8 @@ class SliderSetting(
val selectedFloat: Float
get() {
val setting = setting ?: return defaultValue!!.toFloat()
return when (setting) {
val ret = when (setting) {
is AbstractIntSetting -> setting.int.toFloat()
is FloatSetting -> setting.float
is ScaledFloatSetting -> setting.float
@ -36,8 +36,8 @@ class SliderSetting(
-1f
}
}
return ret.coerceIn(min.toFloat(), max.toFloat())
}
/**
* Write a value to the backing int. If that int was previously null,
* initializes a new one and returns it, so it can be added to the Hashmap.

View File

@ -4,14 +4,19 @@
package org.citra.citra_emu.features.settings.ui
import android.net.Uri
import android.os.Bundle
import android.text.TextUtils
import androidx.documentfile.provider.DocumentFile
import org.citra.citra_emu.CitraApplication
import org.citra.citra_emu.NativeLibrary
import org.citra.citra_emu.features.settings.model.IntSetting
import org.citra.citra_emu.features.settings.model.BooleanSetting
import org.citra.citra_emu.features.settings.model.Settings
import org.citra.citra_emu.utils.SystemSaveGame
import org.citra.citra_emu.utils.DirectoryInitialization
import org.citra.citra_emu.utils.FileUtil
import org.citra.citra_emu.utils.Log
import org.citra.citra_emu.utils.PermissionsHandler
import org.citra.citra_emu.utils.TurboHelper
class SettingsActivityPresenter(private val activityView: SettingsActivityView) {
@ -60,6 +65,32 @@ class SettingsActivityPresenter(private val activityView: SettingsActivityView)
loadSettingsUI()
}
private fun updateAndroidImageVisibility() {
val dataDirTreeUri: Uri
val dataDirDocument: DocumentFile
val nomediaFileDocument: DocumentFile?
val nomediaFileExists: Boolean
try {
dataDirTreeUri = PermissionsHandler.citraDirectory
dataDirDocument = DocumentFile.fromTreeUri(CitraApplication.appContext, dataDirTreeUri)!!
nomediaFileDocument = dataDirDocument.findFile(".nomedia")
nomediaFileExists = (nomediaFileDocument != null)
} catch (e: Exception) {
Log.error("[SettingsActivity]: Error occurred while trying to find .nomedia, error: " + e.message)
return
}
if (BooleanSetting.ANDROID_HIDE_IMAGES.boolean) {
if (!nomediaFileExists) {
Log.info("[SettingsActivity]: Attempting to create .nomedia in user data directory")
FileUtil.createFile(dataDirTreeUri.toString(), ".nomedia")
}
} else if (nomediaFileExists) {
Log.info("[SettingsActivity]: Attempting to delete .nomedia in user data directory")
nomediaFileDocument!!.delete()
}
}
fun onStop(finishing: Boolean) {
if (finishing && shouldSave) {
Log.debug("[SettingsActivity] Settings activity stopping. Saving settings to INI...")
@ -67,6 +98,7 @@ class SettingsActivityPresenter(private val activityView: SettingsActivityView)
//added to ensure that layout changes take effect as soon as settings window closes
NativeLibrary.reloadSettings()
NativeLibrary.updateFramebuffer(NativeLibrary.isPortraitMode)
updateAndroidImageVisibility()
TurboHelper.reloadTurbo(false) // TODO: Can this go somewhere else? -OS
}
NativeLibrary.reloadSettings()

View File

@ -248,6 +248,15 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
IntSetting.TURBO_LIMIT.defaultValue.toFloat()
)
)
add(
SwitchSetting(
BooleanSetting.ANDROID_HIDE_IMAGES,
R.string.android_hide_images,
R.string.android_hide_images_description,
BooleanSetting.ANDROID_HIDE_IMAGES.key,
BooleanSetting.ANDROID_HIDE_IMAGES.defaultValue
)
)
}
}
@ -1166,6 +1175,89 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
FloatSetting.LARGE_SCREEN_PROPORTION.defaultValue
)
)
add(
SliderSetting(
FloatSetting.SECOND_SCREEN_OPACITY,
R.string.second_screen_opacity,
R.string.second_screen_opacity_description,
0,
100,
"%",
FloatSetting.SECOND_SCREEN_OPACITY.key,
FloatSetting.SECOND_SCREEN_OPACITY.defaultValue,
isEnabled = IntSetting.SCREEN_LAYOUT.int == 5
)
)
add(HeaderSetting(R.string.bg_color, R.string.bg_color_description))
val bgRedSetting = object : AbstractIntSetting {
override var int: Int
get() = (FloatSetting.BACKGROUND_RED.float * 255).toInt()
set(value) {
FloatSetting.BACKGROUND_RED.float = value.toFloat() / 255
settings.saveSetting(FloatSetting.BACKGROUND_RED, SettingsFile.FILE_NAME_CONFIG)
}
override val key = null
override val section = null
override val isRuntimeEditable = false
override val valueAsString = int.toString()
override val defaultValue = FloatSetting.BACKGROUND_RED.defaultValue
}
add(
SliderSetting(
bgRedSetting,
R.string.bg_red,
0,
0,
255,
""
)
)
val bgGreenSetting = object : AbstractIntSetting {
override var int: Int
get() = (FloatSetting.BACKGROUND_GREEN.float * 255).toInt()
set(value) {
FloatSetting.BACKGROUND_GREEN.float = value.toFloat() / 255
settings.saveSetting(FloatSetting.BACKGROUND_GREEN, SettingsFile.FILE_NAME_CONFIG)
}
override val key = null
override val section = null
override val isRuntimeEditable = false
override val valueAsString = int.toString()
override val defaultValue = FloatSetting.BACKGROUND_GREEN.defaultValue
}
add(
SliderSetting(
bgGreenSetting,
R.string.bg_green,
0,
0,
255,
""
)
)
val bgBlueSetting = object : AbstractIntSetting {
override var int: Int
get() = (FloatSetting.BACKGROUND_BLUE.float * 255).toInt()
set(value) {
FloatSetting.BACKGROUND_BLUE.float = value.toFloat() / 255
settings.saveSetting(FloatSetting.BACKGROUND_BLUE, SettingsFile.FILE_NAME_CONFIG)
}
override val key = null
override val section = null
override val isRuntimeEditable = false
override val valueAsString = int.toString()
override val defaultValue = FloatSetting.BACKGROUND_BLUE.defaultValue
}
add(
SliderSetting(
bgBlueSetting,
R.string.bg_blue,
0,
0,
255,
""
)
)
add(
SubmenuSetting(
R.string.performance_overlay_options,
@ -1201,38 +1293,29 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
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()
},
BooleanSetting.PERF_OVERLAY_ENABLE,
R.string.performance_overlay_enable,
0,
"EmulationMenuSettings_showPerfPerformanceOverlay",
false
BooleanSetting.PERF_OVERLAY_ENABLE.key,
BooleanSetting.PERF_OVERLAY_ENABLE.defaultValue
)
)
add(
SwitchSetting(
BooleanSetting.OVERLAY_BACKGROUND,
R.string.overlay_background,
R.string.overlay_background_description,
"overlay_background",
false
BooleanSetting.PERF_OVERLAY_BACKGROUND,
R.string.performance_overlay_background,
R.string.performance_overlay_background_description,
BooleanSetting.PERF_OVERLAY_BACKGROUND.key,
BooleanSetting.PERF_OVERLAY_BACKGROUND.defaultValue
)
)
add(
SingleChoiceSetting(
IntSetting.PERFORMANCE_OVERLAY_POSITION,
R.string.overlay_position,
R.string.overlay_position_description,
R.string.performance_overlay_position,
R.string.performance_overlay_position_description,
R.array.statsPosition,
R.array.statsPositionValues,
)
@ -1243,61 +1326,61 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
add(
SwitchSetting(
BooleanSetting.OVERLAY_SHOW_FPS,
R.string.overlay_show_fps,
R.string.overlay_show_fps_description,
"overlay_show_fps",
true
BooleanSetting.PERF_OVERLAY_SHOW_FPS,
R.string.performance_overlay_show_fps,
R.string.performance_overlay_show_fps_description,
BooleanSetting.PERF_OVERLAY_SHOW_FPS.key,
BooleanSetting.PERF_OVERLAY_SHOW_FPS.defaultValue
)
)
add(
SwitchSetting(
BooleanSetting.OVERLAY_SHOW_FRAMETIME,
R.string.overlay_show_frametime,
R.string.overlay_show_frametime_description,
"overlay_show_frame_time",
true
BooleanSetting.PERF_OVERLAY_SHOW_FRAMETIME,
R.string.performance_overlay_show_frametime,
R.string.performance_overlay_show_frametime_description,
BooleanSetting.PERF_OVERLAY_SHOW_FRAMETIME.key,
BooleanSetting.PERF_OVERLAY_SHOW_FRAMETIME.defaultValue
)
)
add(
SwitchSetting(
BooleanSetting.OVERLAY_SHOW_SPEED,
R.string.overlay_show_speed,
R.string.overlay_show_speed_description,
"overlay_show_speed",
false
BooleanSetting.PERF_OVERLAY_SHOW_SPEED,
R.string.performance_overlay_show_speed,
R.string.performance_overlay_show_speed_description,
BooleanSetting.PERF_OVERLAY_SHOW_SPEED.key,
BooleanSetting.PERF_OVERLAY_SHOW_SPEED.defaultValue
)
)
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
BooleanSetting.PERF_OVERLAY_SHOW_APP_RAM_USAGE,
R.string.performance_overlay_show_app_ram_usage,
R.string.performance_overlay_show_app_ram_usage_description,
BooleanSetting.PERF_OVERLAY_SHOW_APP_RAM_USAGE.key,
BooleanSetting.PERF_OVERLAY_SHOW_APP_RAM_USAGE.defaultValue
)
)
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
BooleanSetting.PERF_OVERLAY_SHOW_AVAILABLE_RAM,
R.string.performance_overlay_show_available_ram,
R.string.performance_overlay_show_available_ram_description,
BooleanSetting.PERF_OVERLAY_SHOW_AVAILABLE_RAM.key,
BooleanSetting.PERF_OVERLAY_SHOW_AVAILABLE_RAM.defaultValue
)
)
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
BooleanSetting.PERF_OVERLAY_SHOW_BATTERY_TEMP,
R.string.performance_overlay_show_battery_temp,
R.string.performance_overlay_show_battery_temp_description,
BooleanSetting.PERF_OVERLAY_SHOW_BATTERY_TEMP.key,
BooleanSetting.PERF_OVERLAY_SHOW_BATTERY_TEMP.defaultValue
)
)
}

View File

@ -66,6 +66,7 @@ import org.citra.citra_emu.display.ScreenAdjustmentUtil
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.Settings
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.utils.SettingsFile
@ -100,6 +101,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
private val emulationViewModel: EmulationViewModel by activityViewModels()
private val settingsViewModel: SettingsViewModel by viewModels()
private val settings get() = settingsViewModel.settings
private val onPause = Runnable{ togglePause() }
private val onShutdown = Runnable{ emulationState.stop() }
override fun onAttach(context: Context) {
super.onAttach(context)
@ -155,9 +160,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
retainInstance = true
emulationState = EmulationState(game.path)
emulationActivity = requireActivity() as EmulationActivity
screenAdjustmentUtil = ScreenAdjustmentUtil(requireContext(), requireActivity().windowManager, settingsViewModel.settings)
EmulationLifecycleUtil.addShutdownHook(hook = { emulationState.stop() })
EmulationLifecycleUtil.addPauseResumeHook(hook = { togglePause() })
screenAdjustmentUtil = ScreenAdjustmentUtil(requireContext(), requireActivity().windowManager, settings)
EmulationLifecycleUtil.addPauseResumeHook(onPause)
EmulationLifecycleUtil.addShutdownHook(onShutdown)
}
override fun onCreateView(
@ -507,6 +512,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
super.onDetach()
}
override fun onDestroy() {
EmulationLifecycleUtil.removeHook(onPause)
EmulationLifecycleUtil.removeHook(onShutdown)
super.onDestroy()
}
private fun setupCitraDirectoriesThenStartEmulation() {
val directoryInitializationState = DirectoryInitialization.start()
if (directoryInitializationState ===
@ -662,7 +673,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
popupMenu.menu.apply {
findItem(R.id.menu_show_overlay).isChecked = EmulationMenuSettings.showOverlay
findItem(R.id.menu_performance_overlay_show).isChecked =
EmulationMenuSettings.showPerformanceOverlay
BooleanSetting.PERF_OVERLAY_ENABLE.boolean
findItem(R.id.menu_haptic_feedback).isChecked = EmulationMenuSettings.hapticFeedback
findItem(R.id.menu_emulation_joystick_rel_center).isChecked =
EmulationMenuSettings.joystickRelCenter
@ -679,7 +690,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
}
R.id.menu_performance_overlay_show -> {
EmulationMenuSettings.showPerformanceOverlay = !EmulationMenuSettings.showPerformanceOverlay
BooleanSetting.PERF_OVERLAY_ENABLE.boolean = !BooleanSetting.PERF_OVERLAY_ENABLE.boolean
settings.saveSetting(BooleanSetting.PERF_OVERLAY_ENABLE, SettingsFile.FILE_NAME_CONFIG)
updateShowPerformanceOverlay()
true
}
@ -1202,7 +1214,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
}
if (EmulationMenuSettings.showPerformanceOverlay) {
if (BooleanSetting.PERF_OVERLAY_ENABLE.boolean) {
val SYSTEM_FPS = 0
val FPS = 1
val SPEED = 2
@ -1217,11 +1229,11 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
val perfStats = NativeLibrary.getPerfStats()
val dividerString = "\u00A0\u2502 "
if (perfStats[FPS] > 0) {
if (BooleanSetting.OVERLAY_SHOW_FPS.boolean) {
if (BooleanSetting.PERF_OVERLAY_SHOW_FPS.boolean) {
sb.append(String.format("FPS:\u00A0%d", (perfStats[FPS] + 0.5).toInt()))
}
if (BooleanSetting.OVERLAY_SHOW_FRAMETIME.boolean) {
if (BooleanSetting.PERF_OVERLAY_SHOW_FRAMETIME.boolean) {
if (sb.isNotEmpty()) sb.append(dividerString)
sb.append(
String.format(
@ -1236,7 +1248,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
)
}
if (BooleanSetting.OVERLAY_SHOW_SPEED.boolean) {
if (BooleanSetting.PERF_OVERLAY_SHOW_SPEED.boolean) {
if (sb.isNotEmpty()) sb.append(dividerString)
sb.append(
String.format(
@ -1246,14 +1258,14 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
)
}
if (BooleanSetting.OVERLAY_SHOW_APP_RAM_USAGE.boolean) {
if (BooleanSetting.PERF_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 (BooleanSetting.PERF_OVERLAY_SHOW_AVAILABLE_RAM.boolean) {
if (sb.isNotEmpty()) sb.append(dividerString)
context?.let { ctx ->
val activityManager =
@ -1266,14 +1278,14 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
}
}
if (BooleanSetting.OVERLAY_SHOW_BATTERY_TEMP.boolean) {
if (BooleanSetting.PERF_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) {
if (BooleanSetting.PERF_OVERLAY_BACKGROUND.boolean) {
binding.performanceOverlayShowText.setBackgroundResource(R.color.citra_transparent_black)
} else {
binding.performanceOverlayShowText.setBackgroundResource(0)

View File

@ -1,4 +1,4 @@
// Copyright 2023 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -18,15 +18,27 @@ object EmulationLifecycleUtil {
}
fun addShutdownHook(hook: Runnable) {
shutdownHooks.add(hook)
if (shutdownHooks.contains(hook)) {
Log.warning("[EmulationLifecycleUtil] Tried to add shutdown hook for function that already existed. Skipping.")
} else {
shutdownHooks.add(hook)
}
}
fun addPauseResumeHook(hook: Runnable) {
pauseResumeHooks.add(hook)
if (pauseResumeHooks.contains(hook)) {
Log.warning("[EmulationLifecycleUtil] Tried to add pause resume hook for function that already existed. Skipping.")
} else {
pauseResumeHooks.add(hook)
}
}
fun clear() {
pauseResumeHooks.clear()
shutdownHooks.clear()
fun removeHook(hook: Runnable) {
if (pauseResumeHooks.contains(hook)) {
pauseResumeHooks.remove(hook)
}
if (shutdownHooks.contains(hook)) {
shutdownHooks.remove(hook)
}
}
}

View File

@ -35,13 +35,6 @@ object EmulationMenuSettings {
.apply()
}
var showPerformanceOverlay: Boolean
get() = preferences.getBoolean("EmulationMenuSettings_showPerformanceOverlay", false)
set(value) {
preferences.edit()
.putBoolean("EmulationMenuSettings_showPerformanceOverlay", value)
.apply()
}
var hapticFeedback: Boolean
get() = preferences.getBoolean("EmulationMenuSettings_HapticFeedback", true)
set(value) {

View File

@ -172,6 +172,7 @@ void Config::ReadValues() {
ReadSetting("Renderer", Settings::values.bg_red);
ReadSetting("Renderer", Settings::values.bg_green);
ReadSetting("Renderer", Settings::values.bg_blue);
ReadSetting("Renderer", Settings::values.custom_second_layer_opacity);
ReadSetting("Renderer", Settings::values.delay_game_render_thread_us);
ReadSetting("Renderer", Settings::values.disable_right_eye_render);

View File

@ -170,6 +170,9 @@ bg_red =
bg_blue =
bg_green =
# Opacity of second layer when using custom layout option (bottom screen unless swapped). Useful if positioning on top of the first layer.
custom_second_layer_opacity =
# Whether and how Stereoscopic 3D should be rendered
# 0 (default): Off, 1: Side by Side, 2: Reverse Side by Side, 3: Anaglyph, 4: Interlaced, 5: Reverse Interlaced, 6: Cardboard VR
render_3d =

View File

@ -148,12 +148,12 @@
</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>
<item>@string/performance_overlay_position_top_left</item>
<item>@string/performance_overlay_position_center_top</item>
<item>@string/performance_overlay_position_top_right</item>
<item>@string/performance_overlay_position_bottom_left</item>
<item>@string/performance_overlay_position_center_bottom</item>
<item>@string/performance_overlay_position_bottom_right</item>
</string-array>
<integer-array name="statsPositionValues">
<item>0</item>

View File

@ -257,6 +257,8 @@
<string name="frame_limit_enable_description">When enabled, emulation speed will be limited to a specified percentage of normal speed. If disabled, emulation speed will be uncapped and the turbo speed hotkey will not work.</string>
<string name="frame_limit_slider">Limit Speed Percent</string>
<string name="frame_limit_slider_description">Specifies the percentage to limit emulation speed. With the default of 100% emulation will be limited to normal speed. Values higher or lower will increase or decrease the speed limit.</string>
<string name="android_hide_images">Hide 3DS Images from Android</string>
<string name="android_hide_images_description">Prevent 3DS camera, screenshot, and custom texture images from being indexed by Android and displayed in the gallery. Your device may need to be rebooted after changing this setting to take effect.</string>
<string name="turbo_limit">Turbo Speed Limit</string>
<string name="turbo_limit_description">Emulation speed limit used while the turbo hotkey is active.</string>
<string name="expand_to_cutout_area">Expand to Cutout Area</string>
@ -448,6 +450,13 @@
<string name="emulation_portrait_layout_top_full">Default</string>
<string name="emulation_secondary_display_default">System Default (mirror)</string>
<string name="emulation_screen_layout_custom">Custom Layout</string>
<string name="bg_color">Background Color</string>
<string name="bg_color_description">The color which appears behind the screens during emulation, represented as an RGB value.</string>
<string name="bg_red">Red</string>
<string name="bg_green">Green</string>
<string name="bg_blue">Blue</string>
<string name="second_screen_opacity">Custom Layout Second Screen Opacity</string>
<string name="second_screen_opacity_description">The opacity of the second 3DS screen when using a custom screen layout. Useful if the second screen is to be positioned on top of the first screen.</string>
<string name="emulation_small_screen_position">Small Screen Position</string>
<string name="small_screen_position_description">Where should the small screen appear relative to the large one in Large Screen Layout?</string>
<string name="small_screen_position_top_right">Top Right</string>
@ -576,28 +585,28 @@
<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>
<string name="performance_overlay_show_fps">Show FPS</string>
<string name="performance_overlay_show_fps_description">Display current frames per second.</string>
<string name="performance_overlay_show_frametime">Show Frametime</string>
<string name="performance_overlay_show_frametime_description">Display current frametime.</string>
<string name="performance_overlay_show_speed">Show Speed</string>
<string name="performance_overlay_show_speed_description">Display current emulation speed percentage.</string>
<string name="performance_overlay_show_app_ram_usage">Show App Memory Usage</string>
<string name="performance_overlay_show_app_ram_usage_description">Display the amount of RAM getting used by the emulator.</string>
<string name="performance_overlay_show_available_ram">Show Available Memory</string>
<string name="performance_overlay_show_available_ram_description">Display the amount of RAM which is available.</string>
<string name="performance_overlay_show_battery_temp">Show Battery Temperature</string>
<string name="performance_overlay_show_battery_temp_description">Display current Battery temperature in Celsius and Fahrenheit.</string>
<string name="performance_overlay_position">Overlay Position</string>
<string name="performance_overlay_position_description">Choose where the performance overlay is displayed on the screen.</string>
<string name="performance_overlay_position_top_left">Top Left</string>
<string name="performance_overlay_position_top_right">Top Right</string>
<string name="performance_overlay_position_bottom_left">Bottom Left</string>
<string name="performance_overlay_position_bottom_right">Bottom Right</string>
<string name="performance_overlay_position_center_top">Center Top</string>
<string name="performance_overlay_position_center_bottom">Center Bottom</string>
<string name="performance_overlay_background">Overlay Background</string>
<string name="performance_overlay_background_description">Adds a background behind the overlay for easier reading.</string>
<!-- Cheats -->
<string name="cheats">Cheats</string>

View File

@ -60,6 +60,10 @@ if (ENABLE_QT AND UNIX AND NOT APPLE)
target_link_libraries(citra_meta PRIVATE Qt6::DBus gamemode)
endif()
if (ENABLE_QT AND APPLE)
target_link_libraries(citra_meta PRIVATE Qt6::GuiPrivate)
endif()
if (ENABLE_QT AND USE_DISCORD_PRESENCE)
target_link_libraries(citra_meta PRIVATE discord-rpc)
endif()

View File

@ -172,12 +172,12 @@ add_library(citra_qt STATIC EXCLUDE_FROM_ALL
multiplayer/state.h
multiplayer/validation.h
precompiled_headers.h
qt_image_interface.cpp
qt_image_interface.h
uisettings.cpp
uisettings.h
user_data_migration.cpp
user_data_migration.h
qt_image_interface.cpp
qt_image_interface.h
util/clickable_label.cpp
util/clickable_label.h
util/graphics_device_info.cpp
@ -190,6 +190,13 @@ add_library(citra_qt STATIC EXCLUDE_FROM_ALL
util/util.h
)
if (APPLE)
target_sources(citra_qt PUBLIC
qt_swizzle.h
qt_swizzle.mm
)
endif()
file(GLOB COMPAT_LIST
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
@ -275,6 +282,10 @@ if (NOT WIN32)
target_include_directories(citra_qt PRIVATE ${Qt6Gui_PRIVATE_INCLUDE_DIRS})
endif()
if (APPLE)
target_link_libraries(citra_qt PRIVATE Qt6::GuiPrivate)
endif()
if (UNIX AND NOT APPLE)
target_link_libraries(citra_qt PRIVATE Qt6::DBus gamemode)
endif()

View File

@ -69,6 +69,7 @@
#include "citra_qt/movie/movie_record_dialog.h"
#include "citra_qt/multiplayer/state.h"
#include "citra_qt/qt_image_interface.h"
#include "citra_qt/qt_swizzle.h"
#include "citra_qt/uisettings.h"
#include "common/play_time_manager.h"
#ifdef ENABLE_QT_UPDATE_CHECKER
@ -4112,6 +4113,11 @@ static Qt::HighDpiScaleFactorRoundingPolicy GetHighDpiRoundingPolicy() {
}
void LaunchQtFrontend(int argc, char* argv[]) {
#ifdef __APPLE__
// Ensure that the linker doesn't optimize qt_swizzle.mm out of existence.
QtSwizzle::Dummy();
#endif
Common::DetachedTasks detached_tasks;
#if MICROPROFILE_ENABLED

View File

@ -531,7 +531,7 @@
<item>
<widget class="QLabel" name="lb_opacity_second_layer">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Bottom Screen Opacity % (OpenGL Only)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Bottom Screen Opacity %&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>

View File

@ -0,0 +1,9 @@
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
namespace QtSwizzle {
void Dummy();
} // namespace QtSwizzle

View File

@ -0,0 +1,48 @@
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#import <QtGui/private/qmetallayer_p.h>
#import <objc/runtime.h>
namespace QtSwizzle {
void Dummy() {
// Call this anywhere to make sure that qt_swizzle.mm is linked.
// noop
}
} // namespace QtSwizzle
@implementation QMetalLayer (AzaharPatch)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class targetClass = [self class];
// Get the original and swizzled methods
Method originalMethod =
class_getInstanceMethod(targetClass, @selector(setNeedsDisplayInRect:));
Method swizzledMethod =
class_getInstanceMethod(targetClass, @selector(swizzled_setNeedsDisplayInRect:));
// Swap the implementations
method_exchangeImplementations(originalMethod, swizzledMethod);
});
}
- (void)swizzled_setNeedsDisplayInRect:(CGRect)rect {
constexpr auto tooBig = 1e10; // Arbitrary large number
// Check for problematic huge rectangles
if ((!self.needsDisplay) && (rect.size.width > tooBig || rect.size.height > tooBig ||
rect.origin.x < -tooBig || rect.origin.y < -tooBig)) {
return;
}
// Call the original implementation
[self swizzled_setNeedsDisplayInRect:rect];
}
@end

View File

@ -718,10 +718,7 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout, bool f
}
void RendererOpenGL::ApplySecondLayerOpacity(bool isPortrait) {
// TODO: Allow for second layer opacity in portrait mode android
if (!isPortrait &&
(Settings::values.layout_option.GetValue() == Settings::LayoutOption::CustomLayout) &&
if (Settings::values.layout_option.GetValue() == Settings::LayoutOption::CustomLayout &&
Settings::values.custom_second_layer_opacity.GetValue() < 100) {
state.blend.src_rgb_func = GL_CONSTANT_ALPHA;
state.blend.src_a_func = GL_CONSTANT_ALPHA;
@ -732,8 +729,7 @@ void RendererOpenGL::ApplySecondLayerOpacity(bool isPortrait) {
}
void RendererOpenGL::ResetSecondLayerOpacity(bool isPortrait) {
if (!isPortrait &&
(Settings::values.layout_option.GetValue() == Settings::LayoutOption::CustomLayout) &&
if (Settings::values.layout_option.GetValue() == Settings::LayoutOption::CustomLayout &&
Settings::values.custom_second_layer_opacity.GetValue() < 100) {
state.blend.src_rgb_func = GL_ONE;
state.blend.dst_rgb_func = GL_ZERO;

View File

@ -376,7 +376,13 @@ void RendererVulkan::BuildPipelines() {
};
const vk::PipelineColorBlendAttachmentState colorblend_attachment = {
.blendEnable = false,
.blendEnable = true,
.srcColorBlendFactor = vk::BlendFactor::eConstantAlpha,
.dstColorBlendFactor = vk::BlendFactor::eOneMinusConstantAlpha,
.colorBlendOp = vk::BlendOp::eAdd,
.srcAlphaBlendFactor = vk::BlendFactor::eConstantAlpha,
.dstAlphaBlendFactor = vk::BlendFactor::eOneMinusConstantAlpha,
.alphaBlendOp = vk::BlendOp::eAdd,
.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG |
vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA,
};
@ -385,7 +391,6 @@ void RendererVulkan::BuildPipelines() {
.logicOpEnable = false,
.attachmentCount = 1,
.pAttachments = &colorblend_attachment,
.blendConstants = std::array{1.0f, 1.0f, 1.0f, 1.0f},
};
const vk::Viewport placeholder_viewport = vk::Viewport{0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f};
@ -398,6 +403,7 @@ void RendererVulkan::BuildPipelines() {
};
const std::array dynamic_states = {
vk::DynamicState::eBlendConstants,
vk::DynamicState::eViewport,
vk::DynamicState::eScissor,
};
@ -729,6 +735,13 @@ void RendererVulkan::DrawSingleScreenStereo(u32 screen_id_l, u32 screen_id_r, fl
});
}
void RendererVulkan::ApplySecondLayerOpacity(float alpha) {
scheduler.Record([alpha](vk::CommandBuffer cmdbuf) {
const std::array<float, 4> blend_constants = {0.0f, 0.0f, 0.0f, alpha};
cmdbuf.setBlendConstants(blend_constants.data());
});
}
void RendererVulkan::DrawTopScreen(const Layout::FramebufferLayout& layout,
const Common::Rectangle<u32>& top_screen) {
if (!layout.top_screen_enabled) {
@ -867,13 +880,30 @@ void RendererVulkan::DrawScreens(Frame* frame, const Layout::FramebufferLayout&
draw_info.modelview = MakeOrthographicMatrix(layout.width, layout.height);
draw_info.layer = 0;
// Apply the initial default opacity value; Needed to avoid flickering
ApplySecondLayerOpacity(1.0f);
bool use_custom_opacity =
Settings::values.layout_option.GetValue() == Settings::LayoutOption::CustomLayout &&
Settings::values.custom_second_layer_opacity.GetValue() < 100;
float second_alpha = use_custom_opacity
? Settings::values.custom_second_layer_opacity.GetValue() / 100.0f
: 1.0f;
if (!Settings::values.swap_screen.GetValue()) {
DrawTopScreen(layout, top_screen);
draw_info.layer = 0;
if (use_custom_opacity) {
ApplySecondLayerOpacity(second_alpha);
}
DrawBottomScreen(layout, bottom_screen);
} else {
DrawBottomScreen(layout, bottom_screen);
draw_info.layer = 0;
if (use_custom_opacity) {
ApplySecondLayerOpacity(second_alpha);
}
DrawTopScreen(layout, top_screen);
}

View File

@ -102,12 +102,16 @@ private:
void DrawScreens(Frame* frame, const Layout::FramebufferLayout& layout, bool flipped);
void DrawBottomScreen(const Layout::FramebufferLayout& layout,
const Common::Rectangle<u32>& bottom_screen);
void DrawTopScreen(const Layout::FramebufferLayout& layout,
const Common::Rectangle<u32>& top_screen);
void DrawSingleScreen(u32 screen_id, float x, float y, float w, float h,
Layout::DisplayOrientation orientation);
void DrawSingleScreenStereo(u32 screen_id_l, u32 screen_id_r, float x, float y, float w,
float h, Layout::DisplayOrientation orientation);
void ApplySecondLayerOpacity(float alpha);
void LoadFBToScreenInfo(const Pica::FramebufferConfig& framebuffer, ScreenInfo& screen_info,
bool right_eye);
void FillScreen(Common::Vec3<u8> color, const TextureInfo& texture);

View File

@ -556,12 +556,15 @@ bool PipelineCache::EnsureDirectories() const {
};
return create_dir(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir)) &&
create_dir(GetPipelineCacheDir());
create_dir(GetVulkanDir()) && create_dir(GetPipelineCacheDir());
}
std::string PipelineCache::GetVulkanDir() const {
return FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir) + "vulkan" + DIR_SEP;
}
std::string PipelineCache::GetPipelineCacheDir() const {
return FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir) + "vulkan" + DIR_SEP + "pipeline" +
DIR_SEP;
return GetVulkanDir() + "pipeline" + DIR_SEP;
}
void PipelineCache::SwitchPipelineCache(u64 title_id, const std::atomic_bool& stop_loading,

View File

@ -108,6 +108,9 @@ private:
/// Create pipeline cache directories. Returns true on success.
bool EnsureDirectories() const;
/// Returns the Vulkan shader directory
std::string GetVulkanDir() const;
/// Returns the pipeline cache storage dir
std::string GetPipelineCacheDir() const;

View File

@ -7,7 +7,7 @@ The scripts in this directory assume that your current working directory is the
## Pre-release checklist
- [ ] Update compatibility list
- [ ] Update translations
- [ ] If this is a major release (2123.1 -> major.minor), update translations
### Note:

6
tools/purge-github-cache.sh Executable file
View File

@ -0,0 +1,6 @@
#!/bin/bash -ex
# This script assumes that the Github CLI is installed and that
# the authenticated user has appropriate authorization.
gh cache delete --all