mirror of
https://github.com/azahar-emu/azahar
synced 2025-11-07 07:29:58 +01:00
renderer: Add disable right eye performance hack
This commit is contained in:
parent
189dccda83
commit
758ded7fae
@ -63,7 +63,8 @@ enum class IntSetting(
|
|||||||
USE_FRAME_LIMIT("use_frame_limit", Settings.SECTION_RENDERER, 1),
|
USE_FRAME_LIMIT("use_frame_limit", Settings.SECTION_RENDERER, 1),
|
||||||
DELAY_RENDER_THREAD_US("delay_game_render_thread_us", Settings.SECTION_RENDERER, 0),
|
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),
|
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 var int: Int = defaultValue
|
||||||
|
|
||||||
override val valueAsString: String
|
override val valueAsString: String
|
||||||
@ -91,7 +92,8 @@ enum class IntSetting(
|
|||||||
CPU_JIT,
|
CPU_JIT,
|
||||||
ASYNC_CUSTOM_LOADING,
|
ASYNC_CUSTOM_LOADING,
|
||||||
AUDIO_INPUT_TYPE,
|
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 }
|
fun from(key: String): IntSetting? = IntSetting.values().firstOrNull { it.key == key }
|
||||||
|
|||||||
@ -847,6 +847,15 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
|||||||
IntSetting.STEREOSCOPIC_3D_DEPTH.defaultValue.toFloat()
|
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(HeaderSetting(R.string.cardboard_vr))
|
||||||
add(
|
add(
|
||||||
|
|||||||
@ -1138,9 +1138,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
|||||||
val perfStats = NativeLibrary.getPerfStats()
|
val perfStats = NativeLibrary.getPerfStats()
|
||||||
if (perfStats[FPS] > 0) {
|
if (perfStats[FPS] > 0) {
|
||||||
binding.showFpsText.text = String.format(
|
binding.showFpsText.text = String.format(
|
||||||
"FPS: %d Speed: %d%%",
|
"FPS: %d Speed: %d%% FT: %.2fms",
|
||||||
(perfStats[FPS] + 0.5).toInt(),
|
(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)
|
perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 3000)
|
||||||
|
|||||||
@ -20,8 +20,6 @@ add_library(citra-android SHARED
|
|||||||
emu_window/emu_window.cpp
|
emu_window/emu_window.cpp
|
||||||
emu_window/emu_window.h
|
emu_window/emu_window.h
|
||||||
game_info.cpp
|
game_info.cpp
|
||||||
game_settings.cpp
|
|
||||||
game_settings.h
|
|
||||||
id_cache.cpp
|
id_cache.cpp
|
||||||
id_cache.h
|
id_cache.h
|
||||||
native.cpp
|
native.cpp
|
||||||
|
|||||||
@ -172,6 +172,7 @@ void Config::ReadValues() {
|
|||||||
ReadSetting("Renderer", Settings::values.bg_green);
|
ReadSetting("Renderer", Settings::values.bg_green);
|
||||||
ReadSetting("Renderer", Settings::values.bg_blue);
|
ReadSetting("Renderer", Settings::values.bg_blue);
|
||||||
ReadSetting("Renderer", Settings::values.delay_game_render_thread_us);
|
ReadSetting("Renderer", Settings::values.delay_game_render_thread_us);
|
||||||
|
ReadSetting("Renderer", Settings::values.disable_right_eye_render);
|
||||||
|
|
||||||
// Layout
|
// Layout
|
||||||
// Somewhat inelegant solution to ensure layout value is between 0 and 5 on read
|
// Somewhat inelegant solution to ensure layout value is between 0 and 5 on read
|
||||||
|
|||||||
@ -182,6 +182,11 @@ filter_mode =
|
|||||||
# Set to 0 for no delay, only useful in dynamic-fps games to simulate GPU delay.
|
# Set to 0 for no delay, only useful in dynamic-fps games to simulate GPU delay.
|
||||||
delay_game_render_thread_us =
|
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]
|
||||||
# Layout for the screen inside the render window, landscape mode
|
# Layout for the screen inside the render window, landscape mode
|
||||||
# 0: Original (screens vertically aligned)
|
# 0: Original (screens vertically aligned)
|
||||||
|
|||||||
@ -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
|
|
||||||
@ -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
|
|
||||||
@ -50,7 +50,6 @@
|
|||||||
#ifdef ENABLE_VULKAN
|
#ifdef ENABLE_VULKAN
|
||||||
#include "jni/emu_window/emu_window_vk.h"
|
#include "jni/emu_window/emu_window_vk.h"
|
||||||
#endif
|
#endif
|
||||||
#include "jni/game_settings.h"
|
|
||||||
#include "jni/id_cache.h"
|
#include "jni/id_cache.h"
|
||||||
#include "jni/input_manager.h"
|
#include "jni/input_manager.h"
|
||||||
#include "jni/ndk_motion.h"
|
#include "jni/ndk_motion.h"
|
||||||
@ -181,7 +180,6 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
|
|||||||
if (app_loader) {
|
if (app_loader) {
|
||||||
app_loader->ReadProgramId(program_id);
|
app_loader->ReadProgramId(program_id);
|
||||||
system.RegisterAppLoaderEarly(app_loader);
|
system.RegisterAppLoaderEarly(app_loader);
|
||||||
GameSettings::LoadOverrides(program_id);
|
|
||||||
}
|
}
|
||||||
system.ApplySettings();
|
system.ApplySettings();
|
||||||
Settings::LogSettings();
|
Settings::LogSettings();
|
||||||
@ -624,7 +622,6 @@ void Java_org_citra_citra_1emu_NativeLibrary_reloadSettings([[maybe_unused]] JNI
|
|||||||
if (system.IsPoweredOn()) {
|
if (system.IsPoweredOn()) {
|
||||||
u64 program_id{};
|
u64 program_id{};
|
||||||
system.GetAppLoader().ReadProgramId(program_id);
|
system.GetAppLoader().ReadProgramId(program_id);
|
||||||
GameSettings::LoadOverrides(program_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
system.ApplySettings();
|
system.ApplySettings();
|
||||||
|
|||||||
@ -671,5 +671,7 @@ Se esperan fallos gráficos temporales cuando ésta esté activado.</string>
|
|||||||
<string name="miscellaneous">Misceláneo</string>
|
<string name="miscellaneous">Misceláneo</string>
|
||||||
<string name="use_artic_base_controller">Usar Artic Controller cuando se está conectado a Artic Base Server</string>
|
<string name="use_artic_base_controller">Usar Artic Controller cuando se está conectado a Artic Base Server</string>
|
||||||
<string name="use_artic_base_controller_desc">Usa los controles proporcionados por Artic Base Server cuando esté conectado a él en lugar del dispositivo de entrada configurado.</string>
|
<string name="use_artic_base_controller_desc">Usa los controles proporcionados por Artic Base Server cuando esté conectado a él en lugar del dispositivo de entrada configurado.</string>
|
||||||
|
<string name="disable_right_eye_render">Desactivar dibujado de ojo derecho</string>
|
||||||
|
<string name="disable_right_eye_render_desc">Mejora considerablemente el rendimiento en algunos juegos, pero puede producir parpadeos en otros.</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@ -782,5 +782,7 @@
|
|||||||
<string name="use_artic_base_controller_desc">Use the controls provided by Artic Base Server when connected to it instead of the configured input device.</string>
|
<string name="use_artic_base_controller_desc">Use the controls provided by Artic Base Server when connected to it instead of the configured input device.</string>
|
||||||
<string name="instant_debug_log">Flush log output on every message</string>
|
<string name="instant_debug_log">Flush log output on every message</string>
|
||||||
<string name="instant_debug_log_desc">Immediately commits the debug log to file. Use this if citra crashes and the log output is being cut.</string>
|
<string name="instant_debug_log_desc">Immediately commits the debug log to file. Use this if citra crashes and the log output is being cut.</string>
|
||||||
|
<string name="disable_right_eye_render">Disable Right Eye Render</string>
|
||||||
|
<string name="disable_right_eye_render_desc">Greatly improves performance in some games, but can cause flickering in others.</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@ -689,6 +689,7 @@ void QtConfig::ReadRendererValues() {
|
|||||||
ReadGlobalSetting(Settings::values.texture_sampling);
|
ReadGlobalSetting(Settings::values.texture_sampling);
|
||||||
|
|
||||||
ReadGlobalSetting(Settings::values.delay_game_render_thread_us);
|
ReadGlobalSetting(Settings::values.delay_game_render_thread_us);
|
||||||
|
ReadGlobalSetting(Settings::values.disable_right_eye_render);
|
||||||
|
|
||||||
if (global) {
|
if (global) {
|
||||||
ReadBasicSetting(Settings::values.use_shader_jit);
|
ReadBasicSetting(Settings::values.use_shader_jit);
|
||||||
@ -1220,6 +1221,7 @@ void QtConfig::SaveRendererValues() {
|
|||||||
WriteGlobalSetting(Settings::values.texture_sampling);
|
WriteGlobalSetting(Settings::values.texture_sampling);
|
||||||
|
|
||||||
WriteGlobalSetting(Settings::values.delay_game_render_thread_us);
|
WriteGlobalSetting(Settings::values.delay_game_render_thread_us);
|
||||||
|
WriteGlobalSetting(Settings::values.disable_right_eye_render);
|
||||||
|
|
||||||
if (global) {
|
if (global) {
|
||||||
WriteSetting(QStringLiteral("use_shader_jit"), Settings::values.use_shader_jit.GetValue(),
|
WriteSetting(QStringLiteral("use_shader_jit"), Settings::values.use_shader_jit.GetValue(),
|
||||||
|
|||||||
@ -66,6 +66,7 @@ void ConfigureEnhancements::SetConfiguration() {
|
|||||||
ui->toggle_custom_textures->setChecked(Settings::values.custom_textures.GetValue());
|
ui->toggle_custom_textures->setChecked(Settings::values.custom_textures.GetValue());
|
||||||
ui->toggle_preload_textures->setChecked(Settings::values.preload_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->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) {
|
void ConfigureEnhancements::updateShaders(Settings::StereoRenderOption stereo_option) {
|
||||||
@ -120,6 +121,7 @@ void ConfigureEnhancements::ApplyConfiguration() {
|
|||||||
Settings::values.pp_shader_name =
|
Settings::values.pp_shader_name =
|
||||||
ui->shader_combobox->itemText(ui->shader_combobox->currentIndex()).toStdString();
|
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,
|
ConfigurationShared::ApplyPerGameSetting(&Settings::values.filter_mode,
|
||||||
ui->toggle_linear_filter, linear_filter);
|
ui->toggle_linear_filter, linear_filter);
|
||||||
@ -133,6 +135,9 @@ void ConfigureEnhancements::ApplyConfiguration() {
|
|||||||
ui->toggle_preload_textures, preload_textures);
|
ui->toggle_preload_textures, preload_textures);
|
||||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.async_custom_loading,
|
ConfigurationShared::ApplyPerGameSetting(&Settings::values.async_custom_loading,
|
||||||
ui->toggle_async_custom_loading, 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() {
|
void ConfigureEnhancements::SetupPerGameUI() {
|
||||||
@ -146,10 +151,15 @@ void ConfigureEnhancements::SetupPerGameUI() {
|
|||||||
ui->toggle_preload_textures->setEnabled(Settings::values.preload_textures.UsingGlobal());
|
ui->toggle_preload_textures->setEnabled(Settings::values.preload_textures.UsingGlobal());
|
||||||
ui->toggle_async_custom_loading->setEnabled(
|
ui->toggle_async_custom_loading->setEnabled(
|
||||||
Settings::values.async_custom_loading.UsingGlobal());
|
Settings::values.async_custom_loading.UsingGlobal());
|
||||||
|
ui->disable_right_eye_render->setEnabled(
|
||||||
|
Settings::values.disable_right_eye_render.UsingGlobal());
|
||||||
return;
|
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);
|
ui->widget_shader->setVisible(false);
|
||||||
|
|
||||||
ConfigurationShared::SetColoredTristate(ui->toggle_linear_filter, Settings::values.filter_mode,
|
ConfigurationShared::SetColoredTristate(ui->toggle_linear_filter, Settings::values.filter_mode,
|
||||||
@ -163,6 +173,9 @@ void ConfigureEnhancements::SetupPerGameUI() {
|
|||||||
ConfigurationShared::SetColoredTristate(ui->toggle_async_custom_loading,
|
ConfigurationShared::SetColoredTristate(ui->toggle_async_custom_loading,
|
||||||
Settings::values.async_custom_loading,
|
Settings::values.async_custom_loading,
|
||||||
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(
|
ConfigurationShared::SetColoredComboBox(
|
||||||
ui->resolution_factor_combobox, ui->widget_resolution,
|
ui->resolution_factor_combobox, ui->widget_resolution,
|
||||||
|
|||||||
@ -43,5 +43,6 @@ private:
|
|||||||
ConfigurationShared::CheckState custom_textures;
|
ConfigurationShared::CheckState custom_textures;
|
||||||
ConfigurationShared::CheckState preload_textures;
|
ConfigurationShared::CheckState preload_textures;
|
||||||
ConfigurationShared::CheckState async_custom_loading;
|
ConfigurationShared::CheckState async_custom_loading;
|
||||||
|
ConfigurationShared::CheckState disable_right_eye_render;
|
||||||
QColor bg_color;
|
QColor bg_color;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -310,6 +310,16 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="disable_right_eye_render">
|
||||||
|
<property name="text">
|
||||||
|
<string>Disable Right Eye Rendering</string>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><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></string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
@ -23,6 +23,7 @@ ConfigureGraphics::ConfigureGraphics(QString gl_renderer, std::span<const QStrin
|
|||||||
ui->physical_device_combo->setEnabled(!is_powered_on);
|
ui->physical_device_combo->setEnabled(!is_powered_on);
|
||||||
ui->toggle_async_shaders->setEnabled(!is_powered_on);
|
ui->toggle_async_shaders->setEnabled(!is_powered_on);
|
||||||
ui->toggle_async_present->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
|
// Set the index to -1 to ensure the below lambda is called with setCurrentIndex
|
||||||
ui->graphics_api_combo->setCurrentIndex(-1);
|
ui->graphics_api_combo->setCurrentIndex(-1);
|
||||||
|
|
||||||
|
|||||||
@ -160,6 +160,7 @@ void SdlConfig::ReadValues() {
|
|||||||
ReadSetting("Renderer", Settings::values.bg_red);
|
ReadSetting("Renderer", Settings::values.bg_red);
|
||||||
ReadSetting("Renderer", Settings::values.bg_green);
|
ReadSetting("Renderer", Settings::values.bg_green);
|
||||||
ReadSetting("Renderer", Settings::values.bg_blue);
|
ReadSetting("Renderer", Settings::values.bg_blue);
|
||||||
|
ReadSetting("Renderer", Settings::values.disable_right_eye_render);
|
||||||
|
|
||||||
// Layout
|
// Layout
|
||||||
ReadSetting("Layout", Settings::values.layout_option);
|
ReadSetting("Layout", Settings::values.layout_option);
|
||||||
|
|||||||
@ -88,6 +88,10 @@ add_library(citra_common STATIC
|
|||||||
file_util.cpp
|
file_util.cpp
|
||||||
file_util.h
|
file_util.h
|
||||||
hash.h
|
hash.h
|
||||||
|
hacks/hack_list.h
|
||||||
|
hacks/hack_list.cpp
|
||||||
|
hacks/hack_manager.h
|
||||||
|
hacks/hack_manager.cpp
|
||||||
literals.h
|
literals.h
|
||||||
logging/backend.cpp
|
logging/backend.cpp
|
||||||
logging/backend.h
|
logging/backend.h
|
||||||
|
|||||||
62
src/common/hacks/hack_list.cpp
Normal file
62
src/common/hacks/hack_list.cpp
Normal file
@ -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
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
|
||||||
|
}};
|
||||||
|
}
|
||||||
18
src/common/hacks/hack_list.h
Normal file
18
src/common/hacks/hack_list.h
Normal file
@ -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
|
||||||
21
src/common/hacks/hack_manager.cpp
Normal file
21
src/common/hacks/hack_manager.cpp
Normal file
@ -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
|
||||||
40
src/common/hacks/hack_manager.h
Normal file
40
src/common/hacks/hack_manager.h
Normal file
@ -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 <map>
|
||||||
|
#include <vector>
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "common/hacks/hack_list.h"
|
||||||
|
|
||||||
|
namespace Common::Hacks {
|
||||||
|
|
||||||
|
enum class HackAllowMode {
|
||||||
|
ALLOW,
|
||||||
|
DISALLOW,
|
||||||
|
FORCE,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HackEntry {
|
||||||
|
HackAllowMode mode{};
|
||||||
|
std::vector<u64> affected_title_ids{};
|
||||||
|
UserHackData* hack_data{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HackManager {
|
||||||
|
const HackEntry* GetHack(const HackType& type, u64 title_id);
|
||||||
|
|
||||||
|
HackAllowMode GetHackAllowMode(const HackType& type, u64 title_id,
|
||||||
|
HackAllowMode default_mode = HackAllowMode::ALLOW) {
|
||||||
|
const HackEntry* hack = GetHack(type, title_id);
|
||||||
|
return (hack != nullptr) ? hack->mode : default_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::multimap<HackType, HackEntry> entries;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern HackManager hack_manager;
|
||||||
|
|
||||||
|
} // namespace Common::Hacks
|
||||||
@ -102,6 +102,7 @@ void LogSettings() {
|
|||||||
log_setting("Renderer_TextureSampling",
|
log_setting("Renderer_TextureSampling",
|
||||||
GetTextureSamplingName(values.texture_sampling.GetValue()));
|
GetTextureSamplingName(values.texture_sampling.GetValue()));
|
||||||
log_setting("Renderer_DelayGameRenderThreasUs", values.delay_game_render_thread_us.GetValue());
|
log_setting("Renderer_DelayGameRenderThreasUs", values.delay_game_render_thread_us.GetValue());
|
||||||
|
log_setting("Renderer_DisableRightEyeRender", values.disable_right_eye_render.GetValue());
|
||||||
log_setting("Stereoscopy_Render3d", values.render_3d.GetValue());
|
log_setting("Stereoscopy_Render3d", values.render_3d.GetValue());
|
||||||
log_setting("Stereoscopy_Factor3d", values.factor_3d.GetValue());
|
log_setting("Stereoscopy_Factor3d", values.factor_3d.GetValue());
|
||||||
log_setting("Stereoscopy_MonoRenderOption", values.mono_render_option.GetValue());
|
log_setting("Stereoscopy_MonoRenderOption", values.mono_render_option.GetValue());
|
||||||
@ -217,6 +218,7 @@ void RestoreGlobalState(bool is_powered_on) {
|
|||||||
values.dump_textures.SetGlobal(true);
|
values.dump_textures.SetGlobal(true);
|
||||||
values.custom_textures.SetGlobal(true);
|
values.custom_textures.SetGlobal(true);
|
||||||
values.preload_textures.SetGlobal(true);
|
values.preload_textures.SetGlobal(true);
|
||||||
|
values.disable_right_eye_render.SetGlobal(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoadProfile(int index) {
|
void LoadProfile(int index) {
|
||||||
|
|||||||
@ -556,6 +556,7 @@ struct Values {
|
|||||||
SwitchableSetting<bool> custom_textures{false, "custom_textures"};
|
SwitchableSetting<bool> custom_textures{false, "custom_textures"};
|
||||||
SwitchableSetting<bool> preload_textures{false, "preload_textures"};
|
SwitchableSetting<bool> preload_textures{false, "preload_textures"};
|
||||||
SwitchableSetting<bool> async_custom_loading{true, "async_custom_loading"};
|
SwitchableSetting<bool> async_custom_loading{true, "async_custom_loading"};
|
||||||
|
SwitchableSetting<bool> disable_right_eye_render{false, "disable_right_eye_render"};
|
||||||
|
|
||||||
// Audio
|
// Audio
|
||||||
bool audio_muted;
|
bool audio_muted;
|
||||||
|
|||||||
@ -266,10 +266,10 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st
|
|||||||
return ResultStatus::ErrorGetLoader;
|
return ResultStatus::ErrorGetLoader;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (restore_plugin_context.has_value() && restore_plugin_context->is_enabled &&
|
|
||||||
restore_plugin_context->use_user_load_parameters) {
|
|
||||||
u64_le program_id = 0;
|
u64_le program_id = 0;
|
||||||
app_loader->ReadProgramId(program_id);
|
app_loader->ReadProgramId(program_id);
|
||||||
|
if (restore_plugin_context.has_value() && restore_plugin_context->is_enabled &&
|
||||||
|
restore_plugin_context->use_user_load_parameters) {
|
||||||
if (restore_plugin_context->user_load_parameters.low_title_Id ==
|
if (restore_plugin_context->user_load_parameters.low_title_Id ==
|
||||||
static_cast<u32_le>(program_id) &&
|
static_cast<u32_le>(program_id) &&
|
||||||
restore_plugin_context->user_load_parameters.plugin_memory_strategy ==
|
restore_plugin_context->user_load_parameters.plugin_memory_strategy ==
|
||||||
@ -312,6 +312,7 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st
|
|||||||
System::Shutdown();
|
System::Shutdown();
|
||||||
return init_result;
|
return init_result;
|
||||||
}
|
}
|
||||||
|
gpu->ReportLoadingProgramID(program_id);
|
||||||
|
|
||||||
// Restore any parameters that should be carried through a reset.
|
// Restore any parameters that should be carried through a reset.
|
||||||
if (restore_deliver_arg.has_value()) {
|
if (restore_deliver_arg.has_value()) {
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
#include <boost/serialization/shared_ptr.hpp>
|
#include <boost/serialization/shared_ptr.hpp>
|
||||||
#include "common/archives.h"
|
#include "common/archives.h"
|
||||||
#include "common/bit_field.h"
|
#include "common/bit_field.h"
|
||||||
|
#include "common/hacks/hack_manager.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/hle/ipc_helpers.h"
|
#include "core/hle/ipc_helpers.h"
|
||||||
@ -20,6 +21,7 @@
|
|||||||
#include "video_core/gpu.h"
|
#include "video_core/gpu.h"
|
||||||
#include "video_core/gpu_debugger.h"
|
#include "video_core/gpu_debugger.h"
|
||||||
#include "video_core/pica/regs_lcd.h"
|
#include "video_core/pica/regs_lcd.h"
|
||||||
|
#include "video_core/right_eye_disabler.h"
|
||||||
|
|
||||||
SERIALIZE_EXPORT_IMPL(Service::GSP::SessionData)
|
SERIALIZE_EXPORT_IMPL(Service::GSP::SessionData)
|
||||||
SERIALIZE_EXPORT_IMPL(Service::GSP::GSP_GPU)
|
SERIALIZE_EXPORT_IMPL(Service::GSP::GSP_GPU)
|
||||||
@ -599,6 +601,13 @@ Result GSP_GPU::AcquireGpuRight(const Kernel::HLERequestContext& ctx,
|
|||||||
LOG_DEBUG(Service_GSP, "called flag={:08X} process={} thread_id={}", flag, process->process_id,
|
LOG_DEBUG(Service_GSP, "called flag={:08X} process={} thread_id={}", flag, process->process_id,
|
||||||
session_data->thread_id);
|
session_data->thread_id);
|
||||||
|
|
||||||
|
bool right_eye_disable_allow =
|
||||||
|
Common::Hacks::hack_manager.GetHackAllowMode(Common::Hacks::HackType::RIGHT_EYE_DISABLE,
|
||||||
|
process->codeset->program_id) !=
|
||||||
|
Common::Hacks::HackAllowMode::DISALLOW;
|
||||||
|
auto& gpu = system.GPU();
|
||||||
|
gpu.GetRightEyeDisabler().SetEnabled(right_eye_disable_allow);
|
||||||
|
|
||||||
if (active_thread_id == session_data->thread_id) {
|
if (active_thread_id == session_data->thread_id) {
|
||||||
return {ErrorDescription::AlreadyDone, ErrorModule::GX, ErrorSummary::Success,
|
return {ErrorDescription::AlreadyDone, ErrorModule::GX, ErrorSummary::Success,
|
||||||
ErrorLevel::Success};
|
ErrorLevel::Success};
|
||||||
|
|||||||
@ -12,6 +12,7 @@ add_library(video_core STATIC
|
|||||||
gpu.cpp
|
gpu.cpp
|
||||||
gpu.h
|
gpu.h
|
||||||
gpu_debugger.h
|
gpu_debugger.h
|
||||||
|
gpu_impl.h
|
||||||
pica_types.h
|
pica_types.h
|
||||||
precompiled_headers.h
|
precompiled_headers.h
|
||||||
rasterizer_accelerated.cpp
|
rasterizer_accelerated.cpp
|
||||||
@ -63,6 +64,8 @@ add_library(video_core STATIC
|
|||||||
# Needed as a fallback regardless of enabled renderers.
|
# Needed as a fallback regardless of enabled renderers.
|
||||||
renderer_software/sw_blitter.cpp
|
renderer_software/sw_blitter.cpp
|
||||||
renderer_software/sw_blitter.h
|
renderer_software/sw_blitter.h
|
||||||
|
right_eye_disabler.cpp
|
||||||
|
right_eye_disabler.h
|
||||||
shader/debug_data.h
|
shader/debug_data.h
|
||||||
shader/generator/glsl_fs_shader_gen.cpp
|
shader/generator/glsl_fs_shader_gen.cpp
|
||||||
shader/generator/glsl_fs_shader_gen.h
|
shader/generator/glsl_fs_shader_gen.h
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include "common/archives.h"
|
#include "common/archives.h"
|
||||||
|
#include "common/hacks/hack_manager.h"
|
||||||
#include "common/microprofile.h"
|
#include "common/microprofile.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/core_timing.h"
|
#include "core/core_timing.h"
|
||||||
@ -11,10 +12,12 @@
|
|||||||
#include "video_core/debug_utils/debug_utils.h"
|
#include "video_core/debug_utils/debug_utils.h"
|
||||||
#include "video_core/gpu.h"
|
#include "video_core/gpu.h"
|
||||||
#include "video_core/gpu_debugger.h"
|
#include "video_core/gpu_debugger.h"
|
||||||
|
#include "video_core/gpu_impl.h"
|
||||||
#include "video_core/pica/pica_core.h"
|
#include "video_core/pica/pica_core.h"
|
||||||
#include "video_core/pica/regs_lcd.h"
|
#include "video_core/pica/regs_lcd.h"
|
||||||
#include "video_core/renderer_base.h"
|
#include "video_core/renderer_base.h"
|
||||||
#include "video_core/renderer_software/sw_blitter.h"
|
#include "video_core/renderer_software/sw_blitter.h"
|
||||||
|
#include "video_core/right_eye_disabler.h"
|
||||||
#include "video_core/video_core.h"
|
#include "video_core/video_core.h"
|
||||||
|
|
||||||
namespace VideoCore {
|
namespace VideoCore {
|
||||||
@ -25,32 +28,10 @@ constexpr VAddr VADDR_GPU = 0x1EF00000;
|
|||||||
MICROPROFILE_DEFINE(GPU_DisplayTransfer, "GPU", "DisplayTransfer", MP_RGB(100, 100, 255));
|
MICROPROFILE_DEFINE(GPU_DisplayTransfer, "GPU", "DisplayTransfer", MP_RGB(100, 100, 255));
|
||||||
MICROPROFILE_DEFINE(GPU_CmdlistProcessing, "GPU", "Cmdlist Processing", MP_RGB(100, 255, 100));
|
MICROPROFILE_DEFINE(GPU_CmdlistProcessing, "GPU", "Cmdlist Processing", MP_RGB(100, 255, 100));
|
||||||
|
|
||||||
struct GPU::Impl {
|
|
||||||
Core::Timing& timing;
|
|
||||||
Core::System& system;
|
|
||||||
Memory::MemorySystem& memory;
|
|
||||||
std::shared_ptr<Pica::DebugContext> debug_context;
|
|
||||||
Pica::PicaCore pica;
|
|
||||||
GraphicsDebugger gpu_debugger;
|
|
||||||
std::unique_ptr<RendererBase> renderer;
|
|
||||||
RasterizerInterface* rasterizer;
|
|
||||||
std::unique_ptr<SwRenderer::SwBlitter> sw_blitter;
|
|
||||||
Core::TimingEventType* vblank_event;
|
|
||||||
Service::GSP::InterruptHandler signal_interrupt;
|
|
||||||
|
|
||||||
explicit Impl(Core::System& system, Frontend::EmuWindow& emu_window,
|
|
||||||
Frontend::EmuWindow* secondary_window)
|
|
||||||
: timing{system.CoreTiming()}, system{system}, memory{system.Memory()},
|
|
||||||
debug_context{Pica::g_debug_context}, pica{memory, debug_context},
|
|
||||||
renderer{VideoCore::CreateRenderer(emu_window, secondary_window, pica, system)},
|
|
||||||
rasterizer{renderer->Rasterizer()},
|
|
||||||
sw_blitter{std::make_unique<SwRenderer::SwBlitter>(memory, rasterizer)} {}
|
|
||||||
~Impl() = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
GPU::GPU(Core::System& system, Frontend::EmuWindow& emu_window,
|
GPU::GPU(Core::System& system, Frontend::EmuWindow& emu_window,
|
||||||
Frontend::EmuWindow* secondary_window)
|
Frontend::EmuWindow* secondary_window)
|
||||||
: impl{std::make_unique<Impl>(system, emu_window, secondary_window)} {
|
: right_eye_disabler{std::make_unique<RightEyeDisabler>(*this)},
|
||||||
|
impl{std::make_unique<Impl>(system, emu_window, secondary_window)} {
|
||||||
impl->vblank_event = impl->timing.RegisterEvent(
|
impl->vblank_event = impl->timing.RegisterEvent(
|
||||||
"GPU::VBlankCallback",
|
"GPU::VBlankCallback",
|
||||||
[this](uintptr_t user_data, s64 cycles_late) { VBlankCallback(user_data, cycles_late); });
|
[this](uintptr_t user_data, s64 cycles_late) { VBlankCallback(user_data, cycles_late); });
|
||||||
@ -232,6 +213,7 @@ void GPU::SetBufferSwap(u32 screen_id, const Service::GSP::FrameBufferInfo& info
|
|||||||
if (screen_id == 0) {
|
if (screen_id == 0) {
|
||||||
MicroProfileFlip();
|
MicroProfileFlip();
|
||||||
impl->system.perf_stats->EndGameFrame();
|
impl->system.perf_stats->EndGameFrame();
|
||||||
|
right_eye_disabler->ReportEndFrame();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -332,6 +314,26 @@ GraphicsDebugger& GPU::Debugger() {
|
|||||||
return impl->gpu_debugger;
|
return impl->gpu_debugger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GPU::ReportLoadingProgramID(u64 program_ID) {
|
||||||
|
auto hack = Common::Hacks::hack_manager.GetHack(
|
||||||
|
Common::Hacks::HackType::ACCURATE_MULTIPLICATION, program_ID);
|
||||||
|
bool use_accurate_mul = Settings::values.shaders_accurate_mul.GetValue();
|
||||||
|
if (hack) {
|
||||||
|
switch (hack->mode) {
|
||||||
|
case Common::Hacks::HackAllowMode::DISALLOW:
|
||||||
|
use_accurate_mul = false;
|
||||||
|
break;
|
||||||
|
case Common::Hacks::HackAllowMode::FORCE:
|
||||||
|
use_accurate_mul = true;
|
||||||
|
break;
|
||||||
|
case Common::Hacks::HackAllowMode::ALLOW:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl->rasterizer->SetAccurateMul(use_accurate_mul);
|
||||||
|
}
|
||||||
|
|
||||||
void GPU::SubmitCmdList(u32 index) {
|
void GPU::SubmitCmdList(u32 index) {
|
||||||
// Check if a command list was triggered.
|
// Check if a command list was triggered.
|
||||||
auto& config = impl->pica.regs.internal.pipeline.command_buffer;
|
auto& config = impl->pica.regs.internal.pipeline.command_buffer;
|
||||||
@ -344,7 +346,8 @@ void GPU::SubmitCmdList(u32 index) {
|
|||||||
// Forward command list processing to the PICA core.
|
// Forward command list processing to the PICA core.
|
||||||
const PAddr addr = config.GetPhysicalAddress(index);
|
const PAddr addr = config.GetPhysicalAddress(index);
|
||||||
const u32 size = config.GetSize(index);
|
const u32 size = config.GetSize(index);
|
||||||
impl->pica.ProcessCmdList(addr, size);
|
impl->pica.ProcessCmdList(addr, size,
|
||||||
|
!right_eye_disabler->ShouldAllowCmdQueueTrigger(addr, size));
|
||||||
config.trigger[index] = 0;
|
config.trigger[index] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -396,10 +399,13 @@ void GPU::MemoryTransfer() {
|
|||||||
impl->sw_blitter->TextureCopy(config);
|
impl->sw_blitter->TextureCopy(config);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (right_eye_disabler->ShouldAllowDisplayTransfer(config.GetPhysicalInputAddress(),
|
||||||
|
config.input_height)) {
|
||||||
if (!impl->rasterizer->AccelerateDisplayTransfer(config)) {
|
if (!impl->rasterizer->AccelerateDisplayTransfer(config)) {
|
||||||
impl->sw_blitter->DisplayTransfer(config);
|
impl->sw_blitter->DisplayTransfer(config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Complete transfer.
|
// Complete transfer.
|
||||||
config.trigger.Assign(0);
|
config.trigger.Assign(0);
|
||||||
|
|||||||
@ -37,6 +37,7 @@ constexpr u64 FRAME_TICKS = 4481136ull;
|
|||||||
|
|
||||||
class GraphicsDebugger;
|
class GraphicsDebugger;
|
||||||
class RendererBase;
|
class RendererBase;
|
||||||
|
class RightEyeDisabler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The GPU class is the high level interface to the video_core for core services.
|
* The GPU class is the high level interface to the video_core for core services.
|
||||||
@ -92,6 +93,12 @@ public:
|
|||||||
/// Returns a mutable reference to the GSP command debugger.
|
/// Returns a mutable reference to the GSP command debugger.
|
||||||
[[nodiscard]] GraphicsDebugger& Debugger();
|
[[nodiscard]] GraphicsDebugger& Debugger();
|
||||||
|
|
||||||
|
RightEyeDisabler& GetRightEyeDisabler() {
|
||||||
|
return *right_eye_disabler;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReportLoadingProgramID(u64 program_ID);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void SubmitCmdList(u32 index);
|
void SubmitCmdList(u32 index);
|
||||||
|
|
||||||
@ -105,7 +112,10 @@ private:
|
|||||||
template <class Archive>
|
template <class Archive>
|
||||||
void serialize(Archive& ar, const u32 file_version);
|
void serialize(Archive& ar, const u32 file_version);
|
||||||
|
|
||||||
|
std::unique_ptr<RightEyeDisabler> right_eye_disabler;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
friend class RightEyeDisabler;
|
||||||
struct Impl;
|
struct Impl;
|
||||||
std::unique_ptr<Impl> impl;
|
std::unique_ptr<Impl> impl;
|
||||||
|
|
||||||
|
|||||||
48
src/video_core/gpu_impl.h
Normal file
48
src/video_core/gpu_impl.h
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// Copyright 2023 Citra Emulator Project
|
||||||
|
// Copyright 2024 Azahar Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/archives.h"
|
||||||
|
#include "common/microprofile.h"
|
||||||
|
#include "core/core.h"
|
||||||
|
#include "core/core_timing.h"
|
||||||
|
#include "core/hle/service/gsp/gsp_gpu.h"
|
||||||
|
#include "core/hle/service/plgldr/plgldr.h"
|
||||||
|
#include "video_core/debug_utils/debug_utils.h"
|
||||||
|
#include "video_core/gpu.h"
|
||||||
|
#include "video_core/gpu_debugger.h"
|
||||||
|
#include "video_core/gpu_impl.h"
|
||||||
|
#include "video_core/pica/pica_core.h"
|
||||||
|
#include "video_core/pica/regs_lcd.h"
|
||||||
|
#include "video_core/renderer_base.h"
|
||||||
|
#include "video_core/renderer_software/sw_blitter.h"
|
||||||
|
#include "video_core/right_eye_disabler.h"
|
||||||
|
#include "video_core/video_core.h"
|
||||||
|
|
||||||
|
namespace VideoCore {
|
||||||
|
struct GPU::Impl {
|
||||||
|
Core::Timing& timing;
|
||||||
|
Core::System& system;
|
||||||
|
Memory::MemorySystem& memory;
|
||||||
|
std::shared_ptr<Pica::DebugContext> debug_context;
|
||||||
|
Pica::PicaCore pica;
|
||||||
|
GraphicsDebugger gpu_debugger;
|
||||||
|
std::unique_ptr<RendererBase> renderer;
|
||||||
|
RasterizerInterface* rasterizer;
|
||||||
|
std::unique_ptr<SwRenderer::SwBlitter> sw_blitter;
|
||||||
|
Core::TimingEventType* vblank_event;
|
||||||
|
Service::GSP::InterruptHandler signal_interrupt;
|
||||||
|
|
||||||
|
explicit Impl(Core::System& system, Frontend::EmuWindow& emu_window,
|
||||||
|
Frontend::EmuWindow* secondary_window)
|
||||||
|
: timing{system.CoreTiming()}, system{system}, memory{system.Memory()},
|
||||||
|
debug_context{Pica::g_debug_context}, pica{memory, debug_context},
|
||||||
|
renderer{VideoCore::CreateRenderer(emu_window, secondary_window, pica, system)},
|
||||||
|
rasterizer{renderer->Rasterizer()},
|
||||||
|
sw_blitter{std::make_unique<SwRenderer::SwBlitter>(memory, rasterizer)} {}
|
||||||
|
~Impl() = default;
|
||||||
|
};
|
||||||
|
} // namespace VideoCore
|
||||||
@ -91,7 +91,11 @@ void PicaCore::SetInterruptHandler(Service::GSP::InterruptHandler& signal_interr
|
|||||||
this->signal_interrupt = signal_interrupt;
|
this->signal_interrupt = signal_interrupt;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PicaCore::ProcessCmdList(PAddr list, u32 size) {
|
void PicaCore::ProcessCmdList(PAddr list, u32 size, bool ignore_list) {
|
||||||
|
if (ignore_list) {
|
||||||
|
signal_interrupt(Service::GSP::InterruptId::P3D);
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Initialize command list tracking.
|
// Initialize command list tracking.
|
||||||
const u8* head = memory.GetPhysicalPointer(list);
|
const u8* head = memory.GetPhysicalPointer(list);
|
||||||
cmd_list.Reset(list, head, size);
|
cmd_list.Reset(list, head, size);
|
||||||
@ -610,6 +614,73 @@ void PicaCore::LoadVertices(bool is_indexed) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PicaCore::RenderPropertiesGuess PicaCore::GuessCmdRenderProperties(PAddr list, u32 size) {
|
||||||
|
// Initialize command list tracking.
|
||||||
|
const u8* head = memory.GetPhysicalPointer(list);
|
||||||
|
cmd_list.Reset(list, head, size);
|
||||||
|
|
||||||
|
constexpr size_t max_iterations = 0x100;
|
||||||
|
|
||||||
|
RenderPropertiesGuess find_info{};
|
||||||
|
|
||||||
|
find_info.vp_height = regs.internal.rasterizer.viewport_size_y.Value();
|
||||||
|
find_info.paddr = regs.internal.framebuffer.framebuffer.color_buffer_address.Value() * 8;
|
||||||
|
|
||||||
|
auto process_write = [this, &find_info](u32 cmd_id, u32 value) {
|
||||||
|
switch (cmd_id) {
|
||||||
|
case PICA_REG_INDEX(rasterizer.viewport_size_y):
|
||||||
|
find_info.vp_height = value;
|
||||||
|
find_info.vp_heigh_found = true;
|
||||||
|
break;
|
||||||
|
case PICA_REG_INDEX(framebuffer.framebuffer.color_buffer_address):
|
||||||
|
find_info.paddr = value * 8;
|
||||||
|
find_info.paddr_found = true;
|
||||||
|
break;
|
||||||
|
[[unlikely]] case PICA_REG_INDEX(pipeline.command_buffer.trigger[0]) :
|
||||||
|
[[unlikely]] case PICA_REG_INDEX(pipeline.command_buffer.trigger[1]) : {
|
||||||
|
const u32 index =
|
||||||
|
static_cast<u32>(cmd_id - PICA_REG_INDEX(pipeline.command_buffer.trigger[0]));
|
||||||
|
const PAddr addr = regs.internal.pipeline.command_buffer.GetPhysicalAddress(index);
|
||||||
|
const u32 size = regs.internal.pipeline.command_buffer.GetSize(index);
|
||||||
|
const u8* head = memory.GetPhysicalPointer(addr);
|
||||||
|
cmd_list.Reset(addr, head, size);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return find_info.vp_heigh_found && find_info.paddr_found;
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t iterations = 0;
|
||||||
|
while (cmd_list.current_index < cmd_list.length && iterations < max_iterations) {
|
||||||
|
// Align read pointer to 8 bytes
|
||||||
|
if (cmd_list.current_index % 2 != 0) {
|
||||||
|
cmd_list.current_index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the header and the value to write.
|
||||||
|
const u32 value = cmd_list.head[cmd_list.current_index++];
|
||||||
|
const CommandHeader header{cmd_list.head[cmd_list.current_index++]};
|
||||||
|
|
||||||
|
// Write to the requested PICA register.
|
||||||
|
if (process_write(header.cmd_id, value))
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Write any extra paramters as well.
|
||||||
|
for (u32 i = 0; i < header.extra_data_length; ++i) {
|
||||||
|
const u32 cmd = header.cmd_id + (header.group_commands ? i + 1 : 0);
|
||||||
|
const u32 extra_value = cmd_list.head[cmd_list.current_index++];
|
||||||
|
if (process_write(cmd, extra_value))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
iterations++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return find_info;
|
||||||
|
}
|
||||||
|
|
||||||
template <class Archive>
|
template <class Archive>
|
||||||
void PicaCore::CommandList::serialize(Archive& ar, const u32 file_version) {
|
void PicaCore::CommandList::serialize(Archive& ar, const u32 file_version) {
|
||||||
ar& addr;
|
ar& addr;
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
#include "core/hle/service/gsp/gsp_interrupt.h"
|
#include "core/hle/service/gsp/gsp_interrupt.h"
|
||||||
#include "video_core/pica/geometry_pipeline.h"
|
#include "video_core/pica/geometry_pipeline.h"
|
||||||
#include "video_core/pica/packed_attribute.h"
|
#include "video_core/pica/packed_attribute.h"
|
||||||
@ -36,7 +37,7 @@ public:
|
|||||||
|
|
||||||
void SetInterruptHandler(Service::GSP::InterruptHandler& signal_interrupt);
|
void SetInterruptHandler(Service::GSP::InterruptHandler& signal_interrupt);
|
||||||
|
|
||||||
void ProcessCmdList(PAddr list, u32 size);
|
void ProcessCmdList(PAddr list, u32 size, bool ignore_list);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void InitializeRegs();
|
void InitializeRegs();
|
||||||
@ -271,6 +272,16 @@ private:
|
|||||||
ar& cmd_list;
|
ar& cmd_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
struct RenderPropertiesGuess {
|
||||||
|
u32 vp_height;
|
||||||
|
PAddr paddr;
|
||||||
|
bool vp_heigh_found = false;
|
||||||
|
bool paddr_found = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
RenderPropertiesGuess GuessCmdRenderProperties(PAddr list, u32 size);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Memory::MemorySystem& memory;
|
Memory::MemorySystem& memory;
|
||||||
VideoCore::RasterizerInterface* rasterizer;
|
VideoCore::RasterizerInterface* rasterizer;
|
||||||
|
|||||||
@ -82,5 +82,12 @@ public:
|
|||||||
[[maybe_unused]] const DiskResourceLoadCallback& callback) {}
|
[[maybe_unused]] const DiskResourceLoadCallback& callback) {}
|
||||||
|
|
||||||
virtual void SyncEntireState() {}
|
virtual void SyncEntireState() {}
|
||||||
|
|
||||||
|
void SetAccurateMul(bool accurate_mul_) {
|
||||||
|
accurate_mul = accurate_mul_;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool accurate_mul = false;
|
||||||
};
|
};
|
||||||
} // namespace VideoCore
|
} // namespace VideoCore
|
||||||
|
|||||||
@ -175,7 +175,7 @@ void RasterizerOpenGL::TickFrame() {
|
|||||||
|
|
||||||
void RasterizerOpenGL::LoadDiskResources(const std::atomic_bool& stop_loading,
|
void RasterizerOpenGL::LoadDiskResources(const std::atomic_bool& stop_loading,
|
||||||
const VideoCore::DiskResourceLoadCallback& callback) {
|
const VideoCore::DiskResourceLoadCallback& callback) {
|
||||||
shader_manager.LoadDiskCache(stop_loading, callback);
|
shader_manager.LoadDiskCache(stop_loading, callback, accurate_mul);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RasterizerOpenGL::SyncFixedState() {
|
void RasterizerOpenGL::SyncFixedState() {
|
||||||
@ -271,7 +271,7 @@ void RasterizerOpenGL::SetupVertexArray(u8* array_ptr, GLintptr buffer_offset,
|
|||||||
|
|
||||||
bool RasterizerOpenGL::SetupVertexShader() {
|
bool RasterizerOpenGL::SetupVertexShader() {
|
||||||
MICROPROFILE_SCOPE(OpenGL_VS);
|
MICROPROFILE_SCOPE(OpenGL_VS);
|
||||||
return shader_manager.UseProgrammableVertexShader(regs, pica.vs_setup);
|
return shader_manager.UseProgrammableVertexShader(regs, pica.vs_setup, accurate_mul);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RasterizerOpenGL::SetupGeometryShader() {
|
bool RasterizerOpenGL::SetupGeometryShader() {
|
||||||
@ -333,7 +333,7 @@ bool RasterizerOpenGL::AccelerateDrawBatchInternal(bool is_indexed) {
|
|||||||
SetupVertexArray(buffer_ptr, buffer_offset, vs_input_index_min, vs_input_index_max);
|
SetupVertexArray(buffer_ptr, buffer_offset, vs_input_index_min, vs_input_index_max);
|
||||||
vertex_buffer.Unmap(vs_input_size);
|
vertex_buffer.Unmap(vs_input_size);
|
||||||
|
|
||||||
shader_manager.ApplyTo(state);
|
shader_manager.ApplyTo(state, accurate_mul);
|
||||||
state.Apply();
|
state.Apply();
|
||||||
|
|
||||||
if (is_indexed) {
|
if (is_indexed) {
|
||||||
@ -458,7 +458,7 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) {
|
|||||||
state.draw.vertex_buffer = vertex_buffer.GetHandle();
|
state.draw.vertex_buffer = vertex_buffer.GetHandle();
|
||||||
shader_manager.UseTrivialVertexShader();
|
shader_manager.UseTrivialVertexShader();
|
||||||
shader_manager.UseTrivialGeometryShader();
|
shader_manager.UseTrivialGeometryShader();
|
||||||
shader_manager.ApplyTo(state);
|
shader_manager.ApplyTo(state, accurate_mul);
|
||||||
state.Apply();
|
state.Apply();
|
||||||
|
|
||||||
std::size_t max_vertices = 3 * (VERTEX_BUFFER_SIZE / (3 * sizeof(HardwareVertex)));
|
std::size_t max_vertices = 3 * (VERTEX_BUFFER_SIZE / (3 * sizeof(HardwareVertex)));
|
||||||
@ -970,9 +970,10 @@ void RasterizerOpenGL::SyncAndUploadLUTsLF() {
|
|||||||
if (fs_uniform_block_data.fog_lut_dirty || invalidate) {
|
if (fs_uniform_block_data.fog_lut_dirty || invalidate) {
|
||||||
std::array<Common::Vec2f, 128> new_data;
|
std::array<Common::Vec2f, 128> new_data;
|
||||||
|
|
||||||
std::transform(
|
std::transform(pica.fog.lut.begin(), pica.fog.lut.end(), new_data.begin(),
|
||||||
pica.fog.lut.begin(), pica.fog.lut.end(), new_data.begin(),
|
[](const auto& entry) {
|
||||||
[](const auto& entry) { return Common::Vec2f{entry.ToFloat(), entry.DiffToFloat()}; });
|
return Common::Vec2f{entry.ToFloat(), entry.DiffToFloat()};
|
||||||
|
});
|
||||||
|
|
||||||
if (new_data != fog_lut_data || invalidate) {
|
if (new_data != fog_lut_data || invalidate) {
|
||||||
fog_lut_data = new_data;
|
fog_lut_data = new_data;
|
||||||
|
|||||||
@ -82,7 +82,7 @@ static std::set<GLenum> GetSupportedFormats() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static std::tuple<PicaVSConfig, Pica::ShaderSetup> BuildVSConfigFromRaw(
|
static std::tuple<PicaVSConfig, Pica::ShaderSetup> BuildVSConfigFromRaw(
|
||||||
const ShaderDiskCacheRaw& raw, const Driver& driver) {
|
const ShaderDiskCacheRaw& raw, const Driver& driver, bool accurate_mul) {
|
||||||
Pica::ProgramCode program_code{};
|
Pica::ProgramCode program_code{};
|
||||||
Pica::SwizzleData swizzle_data{};
|
Pica::SwizzleData swizzle_data{};
|
||||||
std::copy_n(raw.GetProgramCode().begin(), Pica::MAX_PROGRAM_CODE_LENGTH, program_code.begin());
|
std::copy_n(raw.GetProgramCode().begin(), Pica::MAX_PROGRAM_CODE_LENGTH, program_code.begin());
|
||||||
@ -96,7 +96,7 @@ static std::tuple<PicaVSConfig, Pica::ShaderSetup> BuildVSConfigFromRaw(
|
|||||||
// and care about proper quaternions. Otherwise just use standard vertex+fragment shaders
|
// and care about proper quaternions. Otherwise just use standard vertex+fragment shaders
|
||||||
const bool use_geometry_shader = !raw.GetRawShaderConfig().lighting.disable;
|
const bool use_geometry_shader = !raw.GetRawShaderConfig().lighting.disable;
|
||||||
return {PicaVSConfig{raw.GetRawShaderConfig(), setup, driver.HasClipCullDistance(),
|
return {PicaVSConfig{raw.GetRawShaderConfig(), setup, driver.HasClipCullDistance(),
|
||||||
use_geometry_shader},
|
use_geometry_shader, accurate_mul},
|
||||||
setup};
|
setup};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -337,12 +337,14 @@ ShaderProgramManager::ShaderProgramManager(Frontend::EmuWindow& emu_window_, con
|
|||||||
ShaderProgramManager::~ShaderProgramManager() = default;
|
ShaderProgramManager::~ShaderProgramManager() = default;
|
||||||
|
|
||||||
bool ShaderProgramManager::UseProgrammableVertexShader(const Pica::RegsInternal& regs,
|
bool ShaderProgramManager::UseProgrammableVertexShader(const Pica::RegsInternal& regs,
|
||||||
Pica::ShaderSetup& setup) {
|
Pica::ShaderSetup& setup,
|
||||||
|
bool accurate_mul) {
|
||||||
// Enable the geometry-shader only if we are actually doing per-fragment lighting
|
// Enable the geometry-shader only if we are actually doing per-fragment lighting
|
||||||
// and care about proper quaternions. Otherwise just use standard vertex+fragment shaders
|
// and care about proper quaternions. Otherwise just use standard vertex+fragment shaders
|
||||||
const bool use_geometry_shader = !regs.lighting.disable;
|
const bool use_geometry_shader = !regs.lighting.disable;
|
||||||
|
|
||||||
PicaVSConfig config{regs, setup, driver.HasClipCullDistance(), use_geometry_shader};
|
PicaVSConfig config{regs, setup, driver.HasClipCullDistance(), use_geometry_shader,
|
||||||
|
accurate_mul};
|
||||||
auto [handle, result] = impl->programmable_vertex_shaders.Get(config, setup);
|
auto [handle, result] = impl->programmable_vertex_shaders.Get(config, setup);
|
||||||
if (handle == 0)
|
if (handle == 0)
|
||||||
return false;
|
return false;
|
||||||
@ -358,9 +360,8 @@ bool ShaderProgramManager::UseProgrammableVertexShader(const Pica::RegsInternal&
|
|||||||
const u64 unique_identifier = GetUniqueIdentifier(regs, program_code);
|
const u64 unique_identifier = GetUniqueIdentifier(regs, program_code);
|
||||||
const ShaderDiskCacheRaw raw{unique_identifier, ProgramType::VS, regs,
|
const ShaderDiskCacheRaw raw{unique_identifier, ProgramType::VS, regs,
|
||||||
std::move(program_code)};
|
std::move(program_code)};
|
||||||
const bool sanitize_mul = Settings::values.shaders_accurate_mul.GetValue();
|
|
||||||
disk_cache.SaveRaw(raw);
|
disk_cache.SaveRaw(raw);
|
||||||
disk_cache.SaveDecompiled(unique_identifier, *result, sanitize_mul);
|
disk_cache.SaveDecompiled(unique_identifier, *result, accurate_mul);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -398,7 +399,7 @@ void ShaderProgramManager::UseFragmentShader(const Pica::RegsInternal& regs,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShaderProgramManager::ApplyTo(OpenGLState& state) {
|
void ShaderProgramManager::ApplyTo(OpenGLState& state, bool accurate_mul) {
|
||||||
if (impl->separable) {
|
if (impl->separable) {
|
||||||
if (driver.HasBug(DriverBug::ShaderStageChangeFreeze)) {
|
if (driver.HasBug(DriverBug::ShaderStageChangeFreeze)) {
|
||||||
glUseProgramStages(
|
glUseProgramStages(
|
||||||
@ -418,15 +419,15 @@ void ShaderProgramManager::ApplyTo(OpenGLState& state) {
|
|||||||
cached_program.Create(false,
|
cached_program.Create(false,
|
||||||
std::array{impl->current.vs, impl->current.gs, impl->current.fs});
|
std::array{impl->current.vs, impl->current.gs, impl->current.fs});
|
||||||
auto& disk_cache = impl->disk_cache;
|
auto& disk_cache = impl->disk_cache;
|
||||||
const bool sanitize_mul = Settings::values.shaders_accurate_mul.GetValue();
|
disk_cache.SaveDumpToFile(unique_identifier, cached_program.handle, accurate_mul);
|
||||||
disk_cache.SaveDumpToFile(unique_identifier, cached_program.handle, sanitize_mul);
|
|
||||||
}
|
}
|
||||||
state.draw.shader_program = cached_program.handle;
|
state.draw.shader_program = cached_program.handle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShaderProgramManager::LoadDiskCache(const std::atomic_bool& stop_loading,
|
void ShaderProgramManager::LoadDiskCache(const std::atomic_bool& stop_loading,
|
||||||
const VideoCore::DiskResourceLoadCallback& callback) {
|
const VideoCore::DiskResourceLoadCallback& callback,
|
||||||
|
bool accurate_mul) {
|
||||||
auto& disk_cache = impl->disk_cache;
|
auto& disk_cache = impl->disk_cache;
|
||||||
const auto transferable = disk_cache.LoadTransferable();
|
const auto transferable = disk_cache.LoadTransferable();
|
||||||
if (!transferable) {
|
if (!transferable) {
|
||||||
@ -484,9 +485,8 @@ void ShaderProgramManager::LoadDiskCache(const std::atomic_bool& stop_loading,
|
|||||||
|
|
||||||
if (dump != dump_map.end() && decomp != decompiled_map.end()) {
|
if (dump != dump_map.end() && decomp != decompiled_map.end()) {
|
||||||
// Only load the vertex shader if its sanitize_mul setting matches
|
// Only load the vertex shader if its sanitize_mul setting matches
|
||||||
const bool sanitize_mul = Settings::values.shaders_accurate_mul.GetValue();
|
|
||||||
if (raw.GetProgramType() == ProgramType::VS &&
|
if (raw.GetProgramType() == ProgramType::VS &&
|
||||||
decomp->second.sanitize_mul != sanitize_mul) {
|
decomp->second.sanitize_mul != accurate_mul) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -502,7 +502,7 @@ void ShaderProgramManager::LoadDiskCache(const std::atomic_bool& stop_loading,
|
|||||||
// we have both the binary shader and the decompiled, so inject it into the
|
// we have both the binary shader and the decompiled, so inject it into the
|
||||||
// cache
|
// cache
|
||||||
if (raw.GetProgramType() == ProgramType::VS) {
|
if (raw.GetProgramType() == ProgramType::VS) {
|
||||||
auto [conf, setup] = BuildVSConfigFromRaw(raw, driver);
|
auto [conf, setup] = BuildVSConfigFromRaw(raw, driver, accurate_mul);
|
||||||
std::scoped_lock lock(mutex);
|
std::scoped_lock lock(mutex);
|
||||||
impl->programmable_vertex_shaders.Inject(conf, decomp->second.code,
|
impl->programmable_vertex_shaders.Inject(conf, decomp->second.code,
|
||||||
std::move(shader));
|
std::move(shader));
|
||||||
@ -541,8 +541,7 @@ void ShaderProgramManager::LoadDiskCache(const std::atomic_bool& stop_loading,
|
|||||||
const auto decomp{decompiled_map.find(unique_identifier)};
|
const auto decomp{decompiled_map.find(unique_identifier)};
|
||||||
|
|
||||||
// Only load the program if its sanitize_mul setting matches
|
// Only load the program if its sanitize_mul setting matches
|
||||||
const bool sanitize_mul = Settings::values.shaders_accurate_mul.GetValue();
|
if (decomp->second.sanitize_mul != accurate_mul) {
|
||||||
if (decomp->second.sanitize_mul != sanitize_mul) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -610,7 +609,7 @@ void ShaderProgramManager::LoadDiskCache(const std::atomic_bool& stop_loading,
|
|||||||
// Otherwise decompile and build the shader at boot and save the result to the
|
// Otherwise decompile and build the shader at boot and save the result to the
|
||||||
// precompiled file
|
// precompiled file
|
||||||
if (raw.GetProgramType() == ProgramType::VS) {
|
if (raw.GetProgramType() == ProgramType::VS) {
|
||||||
auto [conf, setup] = BuildVSConfigFromRaw(raw, driver);
|
auto [conf, setup] = BuildVSConfigFromRaw(raw, driver, accurate_mul);
|
||||||
code = GLSL::GenerateVertexShader(setup, conf, impl->separable);
|
code = GLSL::GenerateVertexShader(setup, conf, impl->separable);
|
||||||
OGLShaderStage stage{impl->separable};
|
OGLShaderStage stage{impl->separable};
|
||||||
stage.Create(code.c_str(), GL_VERTEX_SHADER);
|
stage.Create(code.c_str(), GL_VERTEX_SHADER);
|
||||||
|
|||||||
@ -38,9 +38,10 @@ public:
|
|||||||
~ShaderProgramManager();
|
~ShaderProgramManager();
|
||||||
|
|
||||||
void LoadDiskCache(const std::atomic_bool& stop_loading,
|
void LoadDiskCache(const std::atomic_bool& stop_loading,
|
||||||
const VideoCore::DiskResourceLoadCallback& callback);
|
const VideoCore::DiskResourceLoadCallback& callback, bool accurate_mul);
|
||||||
|
|
||||||
bool UseProgrammableVertexShader(const Pica::RegsInternal& config, Pica::ShaderSetup& setup);
|
bool UseProgrammableVertexShader(const Pica::RegsInternal& config, Pica::ShaderSetup& setup,
|
||||||
|
bool accurate_mul);
|
||||||
|
|
||||||
void UseTrivialVertexShader();
|
void UseTrivialVertexShader();
|
||||||
|
|
||||||
@ -50,7 +51,7 @@ public:
|
|||||||
|
|
||||||
void UseFragmentShader(const Pica::RegsInternal& config, const Pica::Shader::UserConfig& user);
|
void UseFragmentShader(const Pica::RegsInternal& config, const Pica::Shader::UserConfig& user);
|
||||||
|
|
||||||
void ApplyTo(OpenGLState& state);
|
void ApplyTo(OpenGLState& state, bool accurate_mul);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Frontend::EmuWindow& emu_window;
|
Frontend::EmuWindow& emu_window;
|
||||||
|
|||||||
@ -334,13 +334,14 @@ bool PipelineCache::BindPipeline(const PipelineInfo& info, bool wait_built) {
|
|||||||
|
|
||||||
bool PipelineCache::UseProgrammableVertexShader(const Pica::RegsInternal& regs,
|
bool PipelineCache::UseProgrammableVertexShader(const Pica::RegsInternal& regs,
|
||||||
Pica::ShaderSetup& setup,
|
Pica::ShaderSetup& setup,
|
||||||
const VertexLayout& layout) {
|
const VertexLayout& layout, bool accurate_mul) {
|
||||||
// Enable the geometry-shader only if we are actually doing per-fragment lighting
|
// Enable the geometry-shader only if we are actually doing per-fragment lighting
|
||||||
// and care about proper quaternions. Otherwise just use standard vertex+fragment shaders.
|
// and care about proper quaternions. Otherwise just use standard vertex+fragment shaders.
|
||||||
// We also don't need the geometry shader if we have the barycentric extension.
|
// We also don't need the geometry shader if we have the barycentric extension.
|
||||||
const bool use_geometry_shader = instance.UseGeometryShaders() && !regs.lighting.disable &&
|
const bool use_geometry_shader = instance.UseGeometryShaders() && !regs.lighting.disable &&
|
||||||
!instance.IsFragmentShaderBarycentricSupported();
|
!instance.IsFragmentShaderBarycentricSupported();
|
||||||
PicaVSConfig config{regs, setup, instance.IsShaderClipDistanceSupported(), use_geometry_shader};
|
PicaVSConfig config{regs, setup, instance.IsShaderClipDistanceSupported(), use_geometry_shader,
|
||||||
|
accurate_mul};
|
||||||
|
|
||||||
for (u32 i = 0; i < layout.attribute_count; i++) {
|
for (u32 i = 0; i < layout.attribute_count; i++) {
|
||||||
const VertexAttribute& attr = layout.attributes[i];
|
const VertexAttribute& attr = layout.attributes[i];
|
||||||
|
|||||||
@ -68,7 +68,7 @@ public:
|
|||||||
|
|
||||||
/// Binds a PICA decompiled vertex shader
|
/// Binds a PICA decompiled vertex shader
|
||||||
bool UseProgrammableVertexShader(const Pica::RegsInternal& regs, Pica::ShaderSetup& setup,
|
bool UseProgrammableVertexShader(const Pica::RegsInternal& regs, Pica::ShaderSetup& setup,
|
||||||
const VertexLayout& layout);
|
const VertexLayout& layout, bool accurate_mul);
|
||||||
|
|
||||||
/// Binds a passthrough vertex shader
|
/// Binds a passthrough vertex shader
|
||||||
void UseTrivialVertexShader();
|
void UseTrivialVertexShader();
|
||||||
|
|||||||
@ -330,7 +330,7 @@ void RasterizerVulkan::SetupFixedAttribs() {
|
|||||||
bool RasterizerVulkan::SetupVertexShader() {
|
bool RasterizerVulkan::SetupVertexShader() {
|
||||||
MICROPROFILE_SCOPE(Vulkan_VS);
|
MICROPROFILE_SCOPE(Vulkan_VS);
|
||||||
return pipeline_cache.UseProgrammableVertexShader(regs, pica.vs_setup,
|
return pipeline_cache.UseProgrammableVertexShader(regs, pica.vs_setup,
|
||||||
pipeline_info.vertex_layout);
|
pipeline_info.vertex_layout, accurate_mul);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RasterizerVulkan::SetupGeometryShader() {
|
bool RasterizerVulkan::SetupGeometryShader() {
|
||||||
@ -963,9 +963,10 @@ void RasterizerVulkan::SyncAndUploadLUTsLF() {
|
|||||||
if (fs_uniform_block_data.fog_lut_dirty || invalidate) {
|
if (fs_uniform_block_data.fog_lut_dirty || invalidate) {
|
||||||
std::array<Common::Vec2f, 128> new_data;
|
std::array<Common::Vec2f, 128> new_data;
|
||||||
|
|
||||||
std::transform(
|
std::transform(pica.fog.lut.begin(), pica.fog.lut.end(), new_data.begin(),
|
||||||
pica.fog.lut.begin(), pica.fog.lut.end(), new_data.begin(),
|
[](const auto& entry) {
|
||||||
[](const auto& entry) { return Common::Vec2f{entry.ToFloat(), entry.DiffToFloat()}; });
|
return Common::Vec2f{entry.ToFloat(), entry.DiffToFloat()};
|
||||||
|
});
|
||||||
|
|
||||||
if (new_data != fog_lut_data || invalidate) {
|
if (new_data != fog_lut_data || invalidate) {
|
||||||
fog_lut_data = new_data;
|
fog_lut_data = new_data;
|
||||||
|
|||||||
75
src/video_core/right_eye_disabler.cpp
Normal file
75
src/video_core/right_eye_disabler.cpp
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
// Copyright 2024 Azahar Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "common/settings.h"
|
||||||
|
#include "right_eye_disabler.h"
|
||||||
|
#include "video_core/gpu.h"
|
||||||
|
#include "video_core/gpu_impl.h"
|
||||||
|
|
||||||
|
namespace VideoCore {
|
||||||
|
bool RightEyeDisabler::ShouldAllowCmdQueueTrigger(PAddr addr, u32 size) {
|
||||||
|
if (!enabled || !enable_for_frame)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
constexpr u32 top_screen_size = 0x00469000;
|
||||||
|
|
||||||
|
if (report_end_frame_pending) {
|
||||||
|
ReportEndFrame();
|
||||||
|
report_end_frame_pending = false;
|
||||||
|
}
|
||||||
|
cmd_queue_trigger_happened = true;
|
||||||
|
|
||||||
|
auto guess = gpu.impl->pica.GuessCmdRenderProperties(addr, size);
|
||||||
|
if (guess.vp_height == top_screen_size && !top_screen_blocked) {
|
||||||
|
if (top_screen_buf == 0) {
|
||||||
|
top_screen_buf = guess.paddr;
|
||||||
|
}
|
||||||
|
top_screen_drawn = true;
|
||||||
|
if (top_screen_transfered) {
|
||||||
|
cmd_trigger_blocked = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_trigger_blocked = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool RightEyeDisabler::ShouldAllowDisplayTransfer(PAddr src_address, size_t size) {
|
||||||
|
if (!enabled || !enable_for_frame)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (size >= 400 && !top_screen_blocked) {
|
||||||
|
if (top_screen_drawn && src_address == top_screen_buf) {
|
||||||
|
top_screen_transfered = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (src_address == top_screen_buf && cmd_trigger_blocked) {
|
||||||
|
top_screen_blocked = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd_queue_trigger_happened)
|
||||||
|
display_tranfer_happened = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
void RightEyeDisabler::ReportEndFrame() {
|
||||||
|
if (!enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
enable_for_frame = Settings::values.disable_right_eye_render.GetValue();
|
||||||
|
|
||||||
|
if (display_tranfer_happened) {
|
||||||
|
top_screen_drawn = false;
|
||||||
|
top_screen_transfered = false;
|
||||||
|
top_screen_blocked = false;
|
||||||
|
cmd_queue_trigger_happened = false;
|
||||||
|
cmd_trigger_blocked = false;
|
||||||
|
display_tranfer_happened = false;
|
||||||
|
top_screen_buf = 0;
|
||||||
|
} else {
|
||||||
|
report_end_frame_pending = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace VideoCore
|
||||||
41
src/video_core/right_eye_disabler.h
Normal file
41
src/video_core/right_eye_disabler.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// 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 VideoCore {
|
||||||
|
class GPU;
|
||||||
|
|
||||||
|
class RightEyeDisabler {
|
||||||
|
public:
|
||||||
|
RightEyeDisabler(GPU& gpu) : gpu{gpu} {}
|
||||||
|
|
||||||
|
bool ShouldAllowCmdQueueTrigger(PAddr addr, u32 size);
|
||||||
|
bool ShouldAllowDisplayTransfer(PAddr src_address, size_t size);
|
||||||
|
|
||||||
|
void ReportEndFrame();
|
||||||
|
|
||||||
|
void SetEnabled(bool enable) {
|
||||||
|
enabled = enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool enabled = true;
|
||||||
|
bool enable_for_frame = true;
|
||||||
|
|
||||||
|
bool top_screen_drawn = false;
|
||||||
|
bool top_screen_transfered = false;
|
||||||
|
bool top_screen_blocked = false;
|
||||||
|
bool cmd_trigger_blocked = false;
|
||||||
|
PAddr top_screen_buf = 0;
|
||||||
|
|
||||||
|
bool cmd_queue_trigger_happened = false;
|
||||||
|
bool display_tranfer_happened = false;
|
||||||
|
bool report_end_frame_pending = false;
|
||||||
|
|
||||||
|
GPU& gpu;
|
||||||
|
};
|
||||||
|
} // namespace VideoCore
|
||||||
@ -37,14 +37,14 @@ void PicaGSConfigState::Init(const Pica::RegsInternal& regs, bool use_clip_plane
|
|||||||
}
|
}
|
||||||
|
|
||||||
void PicaVSConfigState::Init(const Pica::RegsInternal& regs, Pica::ShaderSetup& setup,
|
void PicaVSConfigState::Init(const Pica::RegsInternal& regs, Pica::ShaderSetup& setup,
|
||||||
bool use_clip_planes_, bool use_geometry_shader_) {
|
bool use_clip_planes_, bool use_geometry_shader_, bool accurate_mul_) {
|
||||||
use_clip_planes = use_clip_planes_;
|
use_clip_planes = use_clip_planes_;
|
||||||
use_geometry_shader = use_geometry_shader_;
|
use_geometry_shader = use_geometry_shader_;
|
||||||
|
sanitize_mul = accurate_mul_;
|
||||||
|
|
||||||
program_hash = setup.GetProgramCodeHash();
|
program_hash = setup.GetProgramCodeHash();
|
||||||
swizzle_hash = setup.GetSwizzleDataHash();
|
swizzle_hash = setup.GetSwizzleDataHash();
|
||||||
main_offset = regs.vs.main_offset;
|
main_offset = regs.vs.main_offset;
|
||||||
sanitize_mul = Settings::values.shaders_accurate_mul.GetValue();
|
|
||||||
|
|
||||||
num_outputs = 0;
|
num_outputs = 0;
|
||||||
load_flags.fill(AttribLoadFlags::Float);
|
load_flags.fill(AttribLoadFlags::Float);
|
||||||
@ -60,8 +60,8 @@ void PicaVSConfigState::Init(const Pica::RegsInternal& regs, Pica::ShaderSetup&
|
|||||||
}
|
}
|
||||||
|
|
||||||
PicaVSConfig::PicaVSConfig(const Pica::RegsInternal& regs, Pica::ShaderSetup& setup,
|
PicaVSConfig::PicaVSConfig(const Pica::RegsInternal& regs, Pica::ShaderSetup& setup,
|
||||||
bool use_clip_planes_, bool use_geometry_shader_) {
|
bool use_clip_planes_, bool use_geometry_shader_, bool accurate_mul_) {
|
||||||
state.Init(regs, setup, use_clip_planes_, use_geometry_shader_);
|
state.Init(regs, setup, use_clip_planes_, use_geometry_shader_, accurate_mul_);
|
||||||
}
|
}
|
||||||
|
|
||||||
PicaFixedGSConfig::PicaFixedGSConfig(const Pica::RegsInternal& regs, bool use_clip_planes_) {
|
PicaFixedGSConfig::PicaFixedGSConfig(const Pica::RegsInternal& regs, bool use_clip_planes_) {
|
||||||
|
|||||||
@ -66,7 +66,7 @@ struct PicaGSConfigState {
|
|||||||
*/
|
*/
|
||||||
struct PicaVSConfigState {
|
struct PicaVSConfigState {
|
||||||
void Init(const Pica::RegsInternal& regs, Pica::ShaderSetup& setup, bool use_clip_planes_,
|
void Init(const Pica::RegsInternal& regs, Pica::ShaderSetup& setup, bool use_clip_planes_,
|
||||||
bool use_geometry_shader_);
|
bool use_geometry_shader_, bool accurate_mul_);
|
||||||
|
|
||||||
bool use_clip_planes;
|
bool use_clip_planes;
|
||||||
bool use_geometry_shader;
|
bool use_geometry_shader;
|
||||||
@ -92,7 +92,7 @@ struct PicaVSConfigState {
|
|||||||
*/
|
*/
|
||||||
struct PicaVSConfig : Common::HashableStruct<PicaVSConfigState> {
|
struct PicaVSConfig : Common::HashableStruct<PicaVSConfigState> {
|
||||||
explicit PicaVSConfig(const Pica::RegsInternal& regs, Pica::ShaderSetup& setup,
|
explicit PicaVSConfig(const Pica::RegsInternal& regs, Pica::ShaderSetup& setup,
|
||||||
bool use_clip_planes_, bool use_geometry_shader_);
|
bool use_clip_planes_, bool use_geometry_shader_, bool accurate_mul_);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user