diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/IntSetting.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/IntSetting.kt
index c15234c78..4ba242d16 100644
--- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/IntSetting.kt
+++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/IntSetting.kt
@@ -63,7 +63,8 @@ enum class IntSetting(
USE_FRAME_LIMIT("use_frame_limit", Settings.SECTION_RENDERER, 1),
DELAY_RENDER_THREAD_US("delay_game_render_thread_us", Settings.SECTION_RENDERER, 0),
USE_ARTIC_BASE_CONTROLLER("use_artic_base_controller", Settings.SECTION_CONTROLS, 0),
- ORIENTATION_OPTION("screen_orientation", Settings.SECTION_LAYOUT, 2);
+ ORIENTATION_OPTION("screen_orientation", Settings.SECTION_LAYOUT, 2),
+ DISABLE_RIGHT_EYE_RENDER("disable_right_eye_render", Settings.SECTION_RENDERER, 1);
override var int: Int = defaultValue
override val valueAsString: String
@@ -91,7 +92,8 @@ enum class IntSetting(
CPU_JIT,
ASYNC_CUSTOM_LOADING,
AUDIO_INPUT_TYPE,
- USE_ARTIC_BASE_CONTROLLER
+ USE_ARTIC_BASE_CONTROLLER,
+ SHADERS_ACCURATE_MUL,
)
fun from(key: String): IntSetting? = IntSetting.values().firstOrNull { it.key == key }
diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt
index fade306eb..c124c91aa 100644
--- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt
+++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt
@@ -847,6 +847,15 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
IntSetting.STEREOSCOPIC_3D_DEPTH.defaultValue.toFloat()
)
)
+ add(
+ SwitchSetting(
+ IntSetting.DISABLE_RIGHT_EYE_RENDER,
+ R.string.disable_right_eye_render,
+ R.string.use_disk_shader_cache_description,
+ IntSetting.DISABLE_RIGHT_EYE_RENDER.key,
+ IntSetting.DISABLE_RIGHT_EYE_RENDER.defaultValue
+ )
+ )
add(HeaderSetting(R.string.cardboard_vr))
add(
diff --git a/src/android/app/src/main/java/org/citra/citra_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/citra/citra_emu/fragments/EmulationFragment.kt
index 866940e66..e91876584 100644
--- a/src/android/app/src/main/java/org/citra/citra_emu/fragments/EmulationFragment.kt
+++ b/src/android/app/src/main/java/org/citra/citra_emu/fragments/EmulationFragment.kt
@@ -1138,9 +1138,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
val perfStats = NativeLibrary.getPerfStats()
if (perfStats[FPS] > 0) {
binding.showFpsText.text = String.format(
- "FPS: %d Speed: %d%%",
+ "FPS: %d Speed: %d%% FT: %.2fms",
(perfStats[FPS] + 0.5).toInt(),
- (perfStats[SPEED] * 100.0 + 0.5).toInt()
+ (perfStats[SPEED] * 100.0 + 0.5).toInt(),
+ (perfStats[FRAMETIME] * 1000.0f).toFloat()
)
}
perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 3000)
diff --git a/src/android/app/src/main/jni/CMakeLists.txt b/src/android/app/src/main/jni/CMakeLists.txt
index 7f26f8fb9..f2cad225a 100644
--- a/src/android/app/src/main/jni/CMakeLists.txt
+++ b/src/android/app/src/main/jni/CMakeLists.txt
@@ -20,8 +20,6 @@ add_library(citra-android SHARED
emu_window/emu_window.cpp
emu_window/emu_window.h
game_info.cpp
- game_settings.cpp
- game_settings.h
id_cache.cpp
id_cache.h
native.cpp
diff --git a/src/android/app/src/main/jni/config.cpp b/src/android/app/src/main/jni/config.cpp
index 5462c1cb5..19e422324 100644
--- a/src/android/app/src/main/jni/config.cpp
+++ b/src/android/app/src/main/jni/config.cpp
@@ -172,6 +172,7 @@ void Config::ReadValues() {
ReadSetting("Renderer", Settings::values.bg_green);
ReadSetting("Renderer", Settings::values.bg_blue);
ReadSetting("Renderer", Settings::values.delay_game_render_thread_us);
+ ReadSetting("Renderer", Settings::values.disable_right_eye_render);
// Layout
// Somewhat inelegant solution to ensure layout value is between 0 and 5 on read
diff --git a/src/android/app/src/main/jni/default_ini.h b/src/android/app/src/main/jni/default_ini.h
index 21578b04b..9ec472b12 100644
--- a/src/android/app/src/main/jni/default_ini.h
+++ b/src/android/app/src/main/jni/default_ini.h
@@ -182,6 +182,11 @@ filter_mode =
# Set to 0 for no delay, only useful in dynamic-fps games to simulate GPU delay.
delay_game_render_thread_us =
+# Disables rendering the right eye image.
+# Greatly improves performance in some games, but can cause flickering in others.
+# 0: Enable right eye rendering, 1 (default): Disable right eye rendering
+disable_right_eye_render =
+
[Layout]
# Layout for the screen inside the render window, landscape mode
# 0: Original (screens vertically aligned)
diff --git a/src/android/app/src/main/jni/game_settings.cpp b/src/android/app/src/main/jni/game_settings.cpp
deleted file mode 100644
index d8382b0e1..000000000
--- a/src/android/app/src/main/jni/game_settings.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2019 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include "common/settings.h"
-
-namespace GameSettings {
-
-void LoadOverrides(u64 program_id) {
- switch (program_id) {
- // JAP / The Legend of Zelda: Ocarina of Time 3D
- case 0x0004000000033400:
- // USA / The Legend of Zelda: Ocarina of Time 3D
- case 0x0004000000033500:
- // EUR / The Legend of Zelda: Ocarina of Time 3D
- case 0x0004000000033600:
- // KOR / The Legend of Zelda: Ocarina of Time 3D
- case 0x000400000008F800:
- // CHI / The Legend of Zelda: Ocarina of Time 3D
- case 0x000400000008F900:
- // This game requires accurate multiplication to render properly
- Settings::values.shaders_accurate_mul = true;
- break;
-
- // USA / Mario & Luigi: Superstar Saga + Bowsers Minions
- case 0x00040000001B8F00:
- // EUR / Mario & Luigi: Superstar Saga + Bowsers Minions
- case 0x00040000001B9000:
- // JAP / Mario & Luigi: Superstar Saga + Bowsers Minions
- case 0x0004000000194B00:
- // This game requires accurate multiplication to render properly
- Settings::values.shaders_accurate_mul = true;
- break;
-
- // USA / Mario & Luigi: Bowsers Inside Story + Bowser Jrs Journey
- case 0x00040000001D1400:
- // EUR / Mario & Luigi: Bowsers Inside Story + Bowser Jrs Journey
- case 0x00040000001D1500:
- // This game requires accurate multiplication to render properly
- Settings::values.shaders_accurate_mul = true;
- break;
- }
-}
-
-} // namespace GameSettings
diff --git a/src/android/app/src/main/jni/game_settings.h b/src/android/app/src/main/jni/game_settings.h
deleted file mode 100644
index b034e1865..000000000
--- a/src/android/app/src/main/jni/game_settings.h
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2020 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include "common/common_types.h"
-
-namespace GameSettings {
-
-void LoadOverrides(u64 program_id);
-
-} // namespace GameSettings
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index c61676103..4682df04d 100644
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -50,7 +50,6 @@
#ifdef ENABLE_VULKAN
#include "jni/emu_window/emu_window_vk.h"
#endif
-#include "jni/game_settings.h"
#include "jni/id_cache.h"
#include "jni/input_manager.h"
#include "jni/ndk_motion.h"
@@ -181,7 +180,6 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
if (app_loader) {
app_loader->ReadProgramId(program_id);
system.RegisterAppLoaderEarly(app_loader);
- GameSettings::LoadOverrides(program_id);
}
system.ApplySettings();
Settings::LogSettings();
@@ -624,7 +622,6 @@ void Java_org_citra_citra_1emu_NativeLibrary_reloadSettings([[maybe_unused]] JNI
if (system.IsPoweredOn()) {
u64 program_id{};
system.GetAppLoader().ReadProgramId(program_id);
- GameSettings::LoadOverrides(program_id);
}
system.ApplySettings();
diff --git a/src/android/app/src/main/res/values-es/strings.xml b/src/android/app/src/main/res/values-es/strings.xml
index 73ba58d9d..a3a5c8f1f 100644
--- a/src/android/app/src/main/res/values-es/strings.xml
+++ b/src/android/app/src/main/res/values-es/strings.xml
@@ -671,5 +671,7 @@ Se esperan fallos gráficos temporales cuando ésta esté activado.
Misceláneo
Usar Artic Controller cuando se está conectado a Artic Base Server
Usa los controles proporcionados por Artic Base Server cuando esté conectado a él en lugar del dispositivo de entrada configurado.
+ Desactivar dibujado de ojo derecho
+ Mejora considerablemente el rendimiento en algunos juegos, pero puede producir parpadeos en otros.
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index f90c06e13..3c6c0a5e9 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -782,5 +782,7 @@
Use the controls provided by Artic Base Server when connected to it instead of the configured input device.
Flush log output on every message
Immediately commits the debug log to file. Use this if citra crashes and the log output is being cut.
+ Disable Right Eye Render
+ Greatly improves performance in some games, but can cause flickering in others.
diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp
index f3af3d0a5..1c88bf45c 100644
--- a/src/citra_qt/configuration/config.cpp
+++ b/src/citra_qt/configuration/config.cpp
@@ -689,6 +689,7 @@ void QtConfig::ReadRendererValues() {
ReadGlobalSetting(Settings::values.texture_sampling);
ReadGlobalSetting(Settings::values.delay_game_render_thread_us);
+ ReadGlobalSetting(Settings::values.disable_right_eye_render);
if (global) {
ReadBasicSetting(Settings::values.use_shader_jit);
@@ -1220,6 +1221,7 @@ void QtConfig::SaveRendererValues() {
WriteGlobalSetting(Settings::values.texture_sampling);
WriteGlobalSetting(Settings::values.delay_game_render_thread_us);
+ WriteGlobalSetting(Settings::values.disable_right_eye_render);
if (global) {
WriteSetting(QStringLiteral("use_shader_jit"), Settings::values.use_shader_jit.GetValue(),
diff --git a/src/citra_qt/configuration/configure_enhancements.cpp b/src/citra_qt/configuration/configure_enhancements.cpp
index 91953991c..7d5c6fb2f 100644
--- a/src/citra_qt/configuration/configure_enhancements.cpp
+++ b/src/citra_qt/configuration/configure_enhancements.cpp
@@ -66,6 +66,7 @@ void ConfigureEnhancements::SetConfiguration() {
ui->toggle_custom_textures->setChecked(Settings::values.custom_textures.GetValue());
ui->toggle_preload_textures->setChecked(Settings::values.preload_textures.GetValue());
ui->toggle_async_custom_loading->setChecked(Settings::values.async_custom_loading.GetValue());
+ ui->disable_right_eye_render->setChecked(Settings::values.disable_right_eye_render.GetValue());
}
void ConfigureEnhancements::updateShaders(Settings::StereoRenderOption stereo_option) {
@@ -120,6 +121,7 @@ void ConfigureEnhancements::ApplyConfiguration() {
Settings::values.pp_shader_name =
ui->shader_combobox->itemText(ui->shader_combobox->currentIndex()).toStdString();
}
+ Settings::values.disable_right_eye_render = ui->disable_right_eye_render->isChecked();
ConfigurationShared::ApplyPerGameSetting(&Settings::values.filter_mode,
ui->toggle_linear_filter, linear_filter);
@@ -133,6 +135,9 @@ void ConfigureEnhancements::ApplyConfiguration() {
ui->toggle_preload_textures, preload_textures);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.async_custom_loading,
ui->toggle_async_custom_loading, async_custom_loading);
+ ConfigurationShared::ApplyPerGameSetting(&Settings::values.disable_right_eye_render,
+ ui->disable_right_eye_render,
+ disable_right_eye_render);
}
void ConfigureEnhancements::SetupPerGameUI() {
@@ -146,10 +151,15 @@ void ConfigureEnhancements::SetupPerGameUI() {
ui->toggle_preload_textures->setEnabled(Settings::values.preload_textures.UsingGlobal());
ui->toggle_async_custom_loading->setEnabled(
Settings::values.async_custom_loading.UsingGlobal());
+ ui->disable_right_eye_render->setEnabled(
+ Settings::values.disable_right_eye_render.UsingGlobal());
return;
}
- ui->stereo_group->setVisible(false);
+ ui->render_3d_combobox->setEnabled(false);
+ ui->factor_3d->setEnabled(false);
+ ui->mono_rendering_eye->setEnabled(false);
+
ui->widget_shader->setVisible(false);
ConfigurationShared::SetColoredTristate(ui->toggle_linear_filter, Settings::values.filter_mode,
@@ -163,6 +173,9 @@ void ConfigureEnhancements::SetupPerGameUI() {
ConfigurationShared::SetColoredTristate(ui->toggle_async_custom_loading,
Settings::values.async_custom_loading,
async_custom_loading);
+ ConfigurationShared::SetColoredTristate(ui->disable_right_eye_render,
+ Settings::values.disable_right_eye_render,
+ disable_right_eye_render);
ConfigurationShared::SetColoredComboBox(
ui->resolution_factor_combobox, ui->widget_resolution,
diff --git a/src/citra_qt/configuration/configure_enhancements.h b/src/citra_qt/configuration/configure_enhancements.h
index d366f854a..c9f3449b1 100644
--- a/src/citra_qt/configuration/configure_enhancements.h
+++ b/src/citra_qt/configuration/configure_enhancements.h
@@ -43,5 +43,6 @@ private:
ConfigurationShared::CheckState custom_textures;
ConfigurationShared::CheckState preload_textures;
ConfigurationShared::CheckState async_custom_loading;
+ ConfigurationShared::CheckState disable_right_eye_render;
QColor bg_color;
};
diff --git a/src/citra_qt/configuration/configure_enhancements.ui b/src/citra_qt/configuration/configure_enhancements.ui
index 0a738c227..f87d30167 100644
--- a/src/citra_qt/configuration/configure_enhancements.ui
+++ b/src/citra_qt/configuration/configure_enhancements.ui
@@ -310,6 +310,16 @@
+ -
+
+
+ Disable Right Eye Rendering
+
+
+ <html><head/><body><p>Disable Right Eye Rendering</p><p>Disables rendering the right eye image when not using stereoscopic mode. Greatly improves performance in some games, but can cause flickering in others.</p></body></html>
+
+
+
diff --git a/src/citra_qt/configuration/configure_graphics.cpp b/src/citra_qt/configuration/configure_graphics.cpp
index 2e244b33a..d06f5a97d 100644
--- a/src/citra_qt/configuration/configure_graphics.cpp
+++ b/src/citra_qt/configuration/configure_graphics.cpp
@@ -23,6 +23,7 @@ ConfigureGraphics::ConfigureGraphics(QString gl_renderer, std::spanphysical_device_combo->setEnabled(!is_powered_on);
ui->toggle_async_shaders->setEnabled(!is_powered_on);
ui->toggle_async_present->setEnabled(!is_powered_on);
+ ui->toggle_accurate_mul->setEnabled(!is_powered_on);
// Set the index to -1 to ensure the below lambda is called with setCurrentIndex
ui->graphics_api_combo->setCurrentIndex(-1);
diff --git a/src/citra_sdl/config.cpp b/src/citra_sdl/config.cpp
index 0312c4a24..adb55b4c6 100644
--- a/src/citra_sdl/config.cpp
+++ b/src/citra_sdl/config.cpp
@@ -160,6 +160,7 @@ void SdlConfig::ReadValues() {
ReadSetting("Renderer", Settings::values.bg_red);
ReadSetting("Renderer", Settings::values.bg_green);
ReadSetting("Renderer", Settings::values.bg_blue);
+ ReadSetting("Renderer", Settings::values.disable_right_eye_render);
// Layout
ReadSetting("Layout", Settings::values.layout_option);
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 567831249..85acf1c7a 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -88,6 +88,10 @@ add_library(citra_common STATIC
file_util.cpp
file_util.h
hash.h
+ hacks/hack_list.h
+ hacks/hack_list.cpp
+ hacks/hack_manager.h
+ hacks/hack_manager.cpp
literals.h
logging/backend.cpp
logging/backend.h
diff --git a/src/common/hacks/hack_list.cpp b/src/common/hacks/hack_list.cpp
new file mode 100644
index 000000000..bf1ce6c52
--- /dev/null
+++ b/src/common/hacks/hack_list.cpp
@@ -0,0 +1,62 @@
+// Copyright 2024 Azahar Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/hacks/hack_manager.h"
+
+namespace Common::Hacks {
+
+HackManager hack_manager = {
+ .entries = {
+
+ // The following games cannot use the right eye disable hack due to the way they
+ // handle rendering.
+ {HackType::RIGHT_EYE_DISABLE,
+ HackEntry{
+ .mode = HackAllowMode::DISALLOW,
+ .affected_title_ids =
+ {
+ // Luigi's Mansion
+ 0x00040000001D1900,
+ 0x00040000001D1A00,
+
+ // Luigi's Mansion 2
+ 0x0004000000055F00,
+ 0x0004000000076500,
+ 0x0004000000076400,
+ 0x00040000000D0000,
+
+ // Rayman Origins
+ 0x000400000005A500,
+ 0x0004000000084400,
+ 0x0004000000057600,
+ },
+ }},
+
+ // The following games require accurate multiplication to render properly.
+ {HackType::ACCURATE_MULTIPLICATION,
+ HackEntry{
+ .mode = HackAllowMode::FORCE,
+ .affected_title_ids =
+ {
+ // The Legend of Zelda: Ocarina of Time 3D
+ 0x0004000000033400, // JAP
+ 0x0004000000033500, // USA
+ 0x0004000000033600, // EUR
+ 0x000400000008F800, // KOR
+ 0x000400000008F900, // CHI
+
+ // Mario & Luigi: Superstar Saga + Bowsers Minions
+ 0x00040000001B8F00, // USA
+ 0x00040000001B9000, // EUR
+ 0x0004000000194B00, // JAP
+
+ // Mario & Luigi: Bowsers Inside Story + Bowser Jrs Journey
+ 0x00040000001D1400, // USA
+ 0x00040000001D1500, // EUR
+ 0x00040000001CA900, // JAP
+ },
+ }},
+
+ }};
+}
\ No newline at end of file
diff --git a/src/common/hacks/hack_list.h b/src/common/hacks/hack_list.h
new file mode 100644
index 000000000..d8ea037fd
--- /dev/null
+++ b/src/common/hacks/hack_list.h
@@ -0,0 +1,18 @@
+// Copyright 2024 Azahar Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+
+namespace Common::Hacks {
+
+enum class HackType : int {
+ RIGHT_EYE_DISABLE,
+ ACCURATE_MULTIPLICATION,
+};
+
+class UserHackData {};
+
+} // namespace Common::Hacks
\ No newline at end of file
diff --git a/src/common/hacks/hack_manager.cpp b/src/common/hacks/hack_manager.cpp
new file mode 100644
index 000000000..ae61324ce
--- /dev/null
+++ b/src/common/hacks/hack_manager.cpp
@@ -0,0 +1,21 @@
+// Copyright 2024 Azahar Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "hack_manager.h"
+
+namespace Common::Hacks {
+const HackEntry* HackManager::GetHack(const HackType& type, u64 title_id) {
+ auto range = entries.equal_range(type);
+
+ for (auto it = range.first; it != range.second; it++) {
+ auto tid_found = std::find(it->second.affected_title_ids.begin(),
+ it->second.affected_title_ids.end(), title_id);
+ if (tid_found != it->second.affected_title_ids.end()) {
+ return &it->second;
+ }
+ }
+
+ return nullptr;
+}
+} // namespace Common::Hacks
diff --git a/src/common/hacks/hack_manager.h b/src/common/hacks/hack_manager.h
new file mode 100644
index 000000000..fba859247
--- /dev/null
+++ b/src/common/hacks/hack_manager.h
@@ -0,0 +1,40 @@
+// Copyright 2024 Azahar Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include