diff --git a/android/app/src/main/java/com/github/stenzek/duckstation/GamePropertiesActivity.java b/android/app/src/main/java/com/github/stenzek/duckstation/GamePropertiesActivity.java index e66533e62..9736d40b4 100644 --- a/android/app/src/main/java/com/github/stenzek/duckstation/GamePropertiesActivity.java +++ b/android/app/src/main/java/com/github/stenzek/duckstation/GamePropertiesActivity.java @@ -117,6 +117,7 @@ public class GamePropertiesActivity extends AppCompatActivity { activity.createListGameSetting(ps, "CPUOverclock", R.string.settings_cpu_overclocking, R.array.settings_advanced_cpu_overclock_entries, R.array.settings_advanced_cpu_overclock_values); activity.createListGameSetting(ps, "CDROMReadSpeedup", R.string.settings_cdrom_read_speedup, R.array.settings_cdrom_read_speedup_entries, R.array.settings_cdrom_read_speedup_values); + activity.createListGameSetting(ps, "GPURenderer", R.string.settings_gpu_renderer, R.array.gpu_renderer_entries, R.array.gpu_renderer_values); activity.createListGameSetting(ps, "DisplayAspectRatio", R.string.settings_aspect_ratio, R.array.settings_display_aspect_ratio_names, R.array.settings_display_aspect_ratio_values); activity.createListGameSetting(ps, "DisplayCropMode", R.string.settings_crop_mode, R.array.settings_display_crop_mode_entries, R.array.settings_display_crop_mode_values); activity.createListGameSetting(ps, "GPUDownsampleMode", R.string.settings_downsample_mode, R.array.settings_downsample_mode_entries, R.array.settings_downsample_mode_values); diff --git a/src/duckstation-qt/gamepropertiesdialog.cpp b/src/duckstation-qt/gamepropertiesdialog.cpp index 14d1bbbe9..e08bfceed 100644 --- a/src/duckstation-qt/gamepropertiesdialog.cpp +++ b/src/duckstation-qt/gamepropertiesdialog.cpp @@ -131,6 +131,13 @@ void GamePropertiesDialog::setupAdditionalUi() GameList::GetGameListCompatibilityRatingString(static_cast(i)))); } + m_ui.userRenderer->addItem(tr("(unchanged)")); + for (u32 i = 0; i < static_cast(GPURenderer::Count); i++) + { + m_ui.userRenderer->addItem( + qApp->translate("GPURenderer", Settings::GetRendererDisplayName(static_cast(i)))); + } + m_ui.userAspectRatio->addItem(tr("(unchanged)")); for (u32 i = 0; i < static_cast(DisplayAspectRatio::Count); i++) { @@ -303,6 +310,7 @@ void GamePropertiesDialog::populateGameSettings() } populateBooleanUserSetting(m_ui.userEnableCPUClockSpeedControl, gs.cpu_overclock_enable); + populateBooleanUserSetting(m_ui.userEnable8MBRAM, gs.enable_8mb_ram); updateCPUClockSpeedLabel(); if (gs.cdrom_read_speedup.has_value()) @@ -387,6 +395,12 @@ void GamePropertiesDialog::populateGameSettings() } onUserAspectRatioChanged(); + if (gs.gpu_renderer.has_value()) + { + QSignalBlocker sb(m_ui.userRenderer); + m_ui.userRenderer->setCurrentIndex(static_cast(gs.gpu_renderer.value()) + 1); + } + if (gs.gpu_downsample_mode.has_value()) { QSignalBlocker sb(m_ui.userDownsampleMode); @@ -545,6 +559,7 @@ void GamePropertiesDialog::connectUi() }); connectBooleanUserSetting(m_ui.userEnableCPUClockSpeedControl, &m_game_settings.cpu_overclock_enable); + connectBooleanUserSetting(m_ui.userEnable8MBRAM, &m_game_settings.enable_8mb_ram); connect(m_ui.userEnableCPUClockSpeedControl, &QCheckBox::stateChanged, this, &GamePropertiesDialog::updateCPUClockSpeedLabel); @@ -597,6 +612,14 @@ void GamePropertiesDialog::connectUi() saveGameSettings(); }); + connect(m_ui.userRenderer, QOverload::of(&QComboBox::currentIndexChanged), [this](int index) { + if (index <= 0) + m_game_settings.gpu_renderer.reset(); + else + m_game_settings.gpu_renderer = static_cast(index - 1); + saveGameSettings(); + }); + connect(m_ui.userCropMode, QOverload::of(&QComboBox::currentIndexChanged), [this](int index) { if (index <= 0) m_game_settings.display_crop_mode.reset(); diff --git a/src/duckstation-qt/gamepropertiesdialog.ui b/src/duckstation-qt/gamepropertiesdialog.ui index ee8f3db04..e98d5e3e4 100644 --- a/src/duckstation-qt/gamepropertiesdialog.ui +++ b/src/duckstation-qt/gamepropertiesdialog.ui @@ -6,8 +6,8 @@ 0 0 - 793 - 651 + 769 + 706 @@ -252,6 +252,13 @@ + + + + Enable 8MB RAM (Dev Console) + + + @@ -261,14 +268,14 @@ GPU Screen Display - + Aspect Ratio: - + @@ -302,27 +309,27 @@ - + Crop Mode: - + - + Downsampling: - + - + @@ -346,6 +353,16 @@ + + + + Renderer: + + + + + + diff --git a/src/frontend-common/common_host_interface.cpp b/src/frontend-common/common_host_interface.cpp index e63b0d4c9..189761747 100644 --- a/src/frontend-common/common_host_interface.cpp +++ b/src/frontend-common/common_host_interface.cpp @@ -172,6 +172,8 @@ bool CommonHostInterface::BootSystem(const SystemBootParameters& parameters) if (m_display && m_fullscreen_ui_enabled) FullscreenUI::EnsureGameListLoaded(); + ApplyRendererFromGameSettings(parameters.filename); + if (!HostInterface::BootSystem(parameters)) { // if in batch mode, exit immediately if booting failed @@ -3049,6 +3051,22 @@ void CommonHostInterface::ApplyGameSettings(bool display_osd_messages) gs->ApplySettings(display_osd_messages); } +void CommonHostInterface::ApplyRendererFromGameSettings(const std::string& boot_filename) +{ + if (boot_filename.empty()) + return; + + // we can't use the code here, since it's not loaded yet. but we can cheekily access the game list + const GameListEntry* ge = m_game_list->GetEntryForPath(boot_filename.c_str()); + if (ge && ge->settings.gpu_renderer.has_value() && ge->settings.gpu_renderer.value() != g_settings.gpu_renderer) + { + Log_InfoPrintf("Changing renderer from '%s' to '%s' due to game settings.", + Settings::GetRendererName(g_settings.gpu_renderer), + Settings::GetRendererName(ge->settings.gpu_renderer.value())); + g_settings.gpu_renderer = ge->settings.gpu_renderer.value(); + } +} + void CommonHostInterface::ApplyControllerCompatibilitySettings(u64 controller_mask, bool display_osd_messages) { #define BIT_FOR(ctype) (static_cast(1) << static_cast(ctype)) diff --git a/src/frontend-common/common_host_interface.h b/src/frontend-common/common_host_interface.h index 277073495..5ba50e8ba 100644 --- a/src/frontend-common/common_host_interface.h +++ b/src/frontend-common/common_host_interface.h @@ -415,6 +415,7 @@ protected: void OnHostDisplayResized() override; void ApplyGameSettings(bool display_osd_messages); + void ApplyRendererFromGameSettings(const std::string& boot_filename); void ApplyControllerCompatibilitySettings(u64 controller_mask, bool display_osd_messages); bool CreateHostDisplayResources(); diff --git a/src/frontend-common/game_list.h b/src/frontend-common/game_list.h index 43a6b592e..6a50bb576 100644 --- a/src/frontend-common/game_list.h +++ b/src/frontend-common/game_list.h @@ -130,7 +130,7 @@ private: enum : u32 { GAME_LIST_CACHE_SIGNATURE = 0x45434C47, - GAME_LIST_CACHE_VERSION = 28 + GAME_LIST_CACHE_VERSION = 29 }; using CacheMap = std::unordered_map; diff --git a/src/frontend-common/game_settings.cpp b/src/frontend-common/game_settings.cpp index 91d3b1fe8..406c75e62 100644 --- a/src/frontend-common/game_settings.cpp +++ b/src/frontend-common/game_settings.cpp @@ -108,7 +108,8 @@ bool Entry::LoadFromStream(ByteStream* stream) if (!stream->Read2(bits.data(), num_bytes) || !ReadOptionalFromStream(stream, &runahead_frames) || !ReadOptionalFromStream(stream, &cpu_overclock_numerator) || !ReadOptionalFromStream(stream, &cpu_overclock_denominator) || - !ReadOptionalFromStream(stream, &cpu_overclock_enable) || !ReadOptionalFromStream(stream, &cdrom_read_speedup) || + !ReadOptionalFromStream(stream, &cpu_overclock_enable) || !ReadOptionalFromStream(stream, &enable_8mb_ram) || + !ReadOptionalFromStream(stream, &cdrom_read_speedup) || !ReadOptionalFromStream(stream, &display_active_start_offset) || !ReadOptionalFromStream(stream, &display_active_end_offset) || !ReadOptionalFromStream(stream, &display_line_start_offset) || @@ -124,9 +125,9 @@ bool Entry::LoadFromStream(ByteStream* stream) !ReadOptionalFromStream(stream, &display_force_4_3_for_24bit) || !ReadOptionalFromStream(stream, &display_aspect_ratio_custom_numerator) || !ReadOptionalFromStream(stream, &display_aspect_ratio_custom_denominator) || - !ReadOptionalFromStream(stream, &gpu_resolution_scale) || !ReadOptionalFromStream(stream, &gpu_multisamples) || - !ReadOptionalFromStream(stream, &gpu_per_sample_shading) || !ReadOptionalFromStream(stream, &gpu_true_color) || - !ReadOptionalFromStream(stream, &gpu_scaled_dithering) || + !ReadOptionalFromStream(stream, &gpu_renderer) || !ReadOptionalFromStream(stream, &gpu_resolution_scale) || + !ReadOptionalFromStream(stream, &gpu_multisamples) || !ReadOptionalFromStream(stream, &gpu_per_sample_shading) || + !ReadOptionalFromStream(stream, &gpu_true_color) || !ReadOptionalFromStream(stream, &gpu_scaled_dithering) || !ReadOptionalFromStream(stream, &gpu_force_ntsc_timings) || !ReadOptionalFromStream(stream, &gpu_texture_filter) || !ReadOptionalFromStream(stream, &gpu_widescreen_hack) || !ReadOptionalFromStream(stream, &gpu_pgxp) || !ReadOptionalFromStream(stream, &gpu_pgxp_projection_precision) || @@ -163,7 +164,8 @@ bool Entry::SaveToStream(ByteStream* stream) const return stream->Write2(bits.data(), num_bytes) && WriteOptionalToStream(stream, runahead_frames) && WriteOptionalToStream(stream, cpu_overclock_numerator) && WriteOptionalToStream(stream, cpu_overclock_denominator) && - WriteOptionalToStream(stream, cpu_overclock_enable) && WriteOptionalToStream(stream, cdrom_read_speedup) && + WriteOptionalToStream(stream, cpu_overclock_enable) && WriteOptionalToStream(stream, enable_8mb_ram) && + WriteOptionalToStream(stream, cdrom_read_speedup) && WriteOptionalToStream(stream, display_active_start_offset) && WriteOptionalToStream(stream, display_active_end_offset) && WriteOptionalToStream(stream, display_line_start_offset) && @@ -177,11 +179,12 @@ bool Entry::SaveToStream(ByteStream* stream) const WriteOptionalToStream(stream, display_force_4_3_for_24bit) && WriteOptionalToStream(stream, display_aspect_ratio_custom_numerator) && WriteOptionalToStream(stream, display_aspect_ratio_custom_denominator) && - WriteOptionalToStream(stream, gpu_resolution_scale) && WriteOptionalToStream(stream, gpu_multisamples) && - WriteOptionalToStream(stream, gpu_per_sample_shading) && WriteOptionalToStream(stream, gpu_true_color) && - WriteOptionalToStream(stream, gpu_scaled_dithering) && WriteOptionalToStream(stream, gpu_force_ntsc_timings) && - WriteOptionalToStream(stream, gpu_texture_filter) && WriteOptionalToStream(stream, gpu_widescreen_hack) && - WriteOptionalToStream(stream, gpu_pgxp) && WriteOptionalToStream(stream, gpu_pgxp_projection_precision) && + WriteOptionalToStream(stream, gpu_renderer) && WriteOptionalToStream(stream, gpu_resolution_scale) && + WriteOptionalToStream(stream, gpu_multisamples) && WriteOptionalToStream(stream, gpu_per_sample_shading) && + WriteOptionalToStream(stream, gpu_true_color) && WriteOptionalToStream(stream, gpu_scaled_dithering) && + WriteOptionalToStream(stream, gpu_force_ntsc_timings) && WriteOptionalToStream(stream, gpu_texture_filter) && + WriteOptionalToStream(stream, gpu_widescreen_hack) && WriteOptionalToStream(stream, gpu_pgxp) && + WriteOptionalToStream(stream, gpu_pgxp_projection_precision) && WriteOptionalToStream(stream, gpu_pgxp_depth_buffer) && WriteOptionalToStream(stream, multitap_mode) && WriteOptionalToStream(stream, controller_1_type) && WriteOptionalToStream(stream, controller_2_type) && WriteOptionalToStream(stream, memory_card_1_type) && WriteOptionalToStream(stream, memory_card_2_type) && @@ -210,6 +213,9 @@ static void ParseIniSection(Entry* entry, const char* section, const CSimpleIniA cvalue = ini.GetValue(section, "CPUOverclockEnable", nullptr); if (cvalue) entry->cpu_overclock_enable = StringUtil::FromChars(cvalue); + cvalue = ini.GetValue(section, "Enable8MBRAM", nullptr); + if (cvalue) + entry->enable_8mb_ram = StringUtil::FromChars(cvalue); cvalue = ini.GetValue(section, "CDROMReadSpeedup", nullptr); if (cvalue) @@ -264,6 +270,9 @@ static void ParseIniSection(Entry* entry, const char* section, const CSimpleIniA entry->display_aspect_ratio_custom_denominator = static_cast(std::clamp(lvalue, 1, std::numeric_limits::max())); } + cvalue = ini.GetValue(section, "GPURenderer", nullptr); + if (cvalue) + entry->gpu_renderer = Settings::ParseRendererName(cvalue); cvalue = ini.GetValue(section, "GPUDownsampleMode", nullptr); if (cvalue) entry->gpu_downsample_mode = Settings::ParseDownsampleModeName(cvalue); @@ -355,6 +364,8 @@ static void StoreIniSection(const Entry& entry, const char* section, CSimpleIniA ini.SetLongValue(section, "CPUOverclockDenominator", static_cast(entry.cpu_overclock_denominator.value())); if (entry.cpu_overclock_enable.has_value()) ini.SetBoolValue(section, "CPUOverclockEnable", entry.cpu_overclock_enable.value()); + if (entry.enable_8mb_ram.has_value()) + ini.SetBoolValue(section, "Enable8MBRAM", entry.enable_8mb_ram.value()); if (entry.cdrom_read_speedup.has_value()) ini.SetLongValue(section, "CDROMReadSpeedup", static_cast(entry.cdrom_read_speedup.value())); @@ -397,6 +408,8 @@ static void StoreIniSection(const Entry& entry, const char* section, CSimpleIniA ini.SetLongValue(section, "CustomAspectRatioDenominator", static_cast(entry.display_aspect_ratio_custom_denominator.value())); } + if (entry.gpu_renderer.has_value()) + ini.SetValue(section, "GPURenderer", Settings::GetRendererName(entry.gpu_renderer.value())); if (entry.gpu_downsample_mode.has_value()) { ini.SetValue(section, "GPUDownsampleMode", Settings::GetDownsampleModeName(entry.gpu_downsample_mode.value())); @@ -457,6 +470,7 @@ u32 Entry::GetUserSettingsCount() const count += BoolToUInt32(cpu_overclock_numerator.has_value()); count += BoolToUInt32(cpu_overclock_denominator.has_value()); count += BoolToUInt32(cpu_overclock_enable.has_value()); + count += BoolToUInt32(enable_8mb_ram.has_value()); count += BoolToUInt32(cdrom_read_speedup.has_value()); count += BoolToUInt32(display_crop_mode.has_value()); count += BoolToUInt32(display_aspect_ratio.has_value()); @@ -464,6 +478,7 @@ u32 Entry::GetUserSettingsCount() const count += BoolToUInt32(display_linear_upscaling.has_value()); count += BoolToUInt32(display_integer_upscaling.has_value()); count += BoolToUInt32(display_force_4_3_for_24bit.has_value()); + count += BoolToUInt32(gpu_renderer.has_value()); count += BoolToUInt32(gpu_resolution_scale.has_value()); count += BoolToUInt32(gpu_multisamples.has_value()); count += BoolToUInt32(gpu_per_sample_shading.has_value()); @@ -504,6 +519,13 @@ static std::optional GetEntryValueForKey(const Entry& entry, const return std::to_string(Settings::CPUOverclockFractionToPercent(entry.cpu_overclock_numerator.value_or(1), entry.cpu_overclock_denominator.value_or(1))); } + else if (key == "Enable8MBRAM") + { + if (!entry.enable_8mb_ram.has_value()) + return std::nullopt; + else + return entry.enable_8mb_ram.value() ? "true" : "false"; + } else if (key == "CDROMReadSpeedup") { if (!entry.cdrom_read_speedup.has_value()) @@ -539,6 +561,13 @@ static std::optional GetEntryValueForKey(const Entry& entry, const else return std::to_string(entry.display_aspect_ratio_custom_denominator.value()); } + else if (key == "GPURenderer") + { + if (!entry.gpu_renderer.has_value()) + return std::nullopt; + else + return Settings::GetRendererName(entry.gpu_renderer.value()); + } else if (key == "GPUDownsampleMode") { if (!entry.gpu_downsample_mode.has_value()) @@ -732,6 +761,13 @@ static void SetEntryValueForKey(Entry& entry, const std::string_view& key, const entry.cpu_overclock_enable = true; } } + else if (key == "Enable8MBRAM") + { + if (!value.has_value()) + entry.enable_8mb_ram.reset(); + else + entry.enable_8mb_ram = StringUtil::FromChars(value.value()).value_or(false); + } else if (key == "CDROMReadSpeedup") { if (!value.has_value()) @@ -767,6 +803,13 @@ static void SetEntryValueForKey(Entry& entry, const std::string_view& key, const else entry.display_aspect_ratio_custom_denominator = StringUtil::FromChars(value.value()); } + else if (key == "GPURenderer") + { + if (!value.has_value()) + entry.gpu_renderer.reset(); + else + entry.gpu_renderer = Settings::ParseRendererName(value->c_str()); + } else if (key == "GPUDownsampleMode") { if (!value.has_value()) @@ -1055,6 +1098,8 @@ void Entry::ApplySettings(bool display_osd_messages) const g_settings.cpu_overclock_denominator = cpu_overclock_denominator.value(); if (cpu_overclock_enable.has_value()) g_settings.cpu_overclock_enable = cpu_overclock_enable.value(); + if (enable_8mb_ram.has_value()) + g_settings.enable_8mb_ram = enable_8mb_ram.value(); g_settings.UpdateOverclockActive(); if (cdrom_read_speedup.has_value()) @@ -1098,6 +1143,8 @@ void Entry::ApplySettings(bool display_osd_messages) const if (display_force_4_3_for_24bit.has_value()) g_settings.display_force_4_3_for_24bit = display_force_4_3_for_24bit.value(); + if (gpu_renderer.has_value()) + g_settings.gpu_renderer = gpu_renderer.value(); if (gpu_resolution_scale.has_value()) g_settings.gpu_resolution_scale = gpu_resolution_scale.value(); if (gpu_multisamples.has_value()) diff --git a/src/frontend-common/game_settings.h b/src/frontend-common/game_settings.h index 3995a072a..6276b10fa 100644 --- a/src/frontend-common/game_settings.h +++ b/src/frontend-common/game_settings.h @@ -54,9 +54,11 @@ struct Entry std::optional cpu_overclock_numerator; std::optional cpu_overclock_denominator; std::optional cpu_overclock_enable; + std::optional enable_8mb_ram; std::optional cdrom_read_speedup; std::optional display_crop_mode; std::optional display_aspect_ratio; + std::optional gpu_renderer; std::optional gpu_downsample_mode; std::optional display_linear_upscaling; std::optional display_integer_upscaling;