diff --git a/src/core/host.cpp b/src/core/host.cpp index 0aec4b9b4..f7da14d14 100644 --- a/src/core/host.cpp +++ b/src/core/host.cpp @@ -241,6 +241,13 @@ bool Host::CreateGPUDevice(RenderAPI api) Log_InfoPrintf("Trying to create a %s GPU device...", GPUDevice::RenderAPIToString(api)); g_gpu_device = GPUDevice::CreateDeviceForAPI(api); + std::optional exclusive_fullscreen_control; + if (g_settings.display_exclusive_fullscreen_control != DisplayExclusiveFullscreenControl::Automatic) + { + exclusive_fullscreen_control = + (g_settings.display_exclusive_fullscreen_control == DisplayExclusiveFullscreenControl::Allowed); + } + u32 disabled_features = 0; if (g_settings.gpu_disable_dual_source_blend) disabled_features |= GPUDevice::FEATURE_MASK_DUAL_SOURCE_BLEND; @@ -249,11 +256,12 @@ bool Host::CreateGPUDevice(RenderAPI api) // TODO: FSUI should always use vsync.. const bool vsync = System::IsValid() ? System::ShouldUseVSync() : g_settings.video_sync_enabled; - if (!g_gpu_device || !g_gpu_device->Create( - g_settings.gpu_adapter, - g_settings.gpu_disable_shader_cache ? std::string_view() : std::string_view(EmuFolders::Cache), - SHADER_CACHE_VERSION, g_settings.gpu_use_debug_device, vsync, - g_settings.gpu_threaded_presentation, static_cast(disabled_features))) + if (!g_gpu_device || !g_gpu_device->Create(g_settings.gpu_adapter, + g_settings.gpu_disable_shader_cache ? std::string_view() : + std::string_view(EmuFolders::Cache), + SHADER_CACHE_VERSION, g_settings.gpu_use_debug_device, vsync, + g_settings.gpu_threaded_presentation, exclusive_fullscreen_control, + static_cast(disabled_features))) { Log_ErrorPrintf("Failed to create GPU device."); if (g_gpu_device) diff --git a/src/core/settings.cpp b/src/core/settings.cpp index cdee1b4a1..951507a6b 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -236,6 +236,12 @@ void Settings::Load(SettingsInterface& si) display_scaling = ParseDisplayScaling(si.GetStringValue("Display", "Scaling", GetDisplayScalingName(DEFAULT_DISPLAY_SCALING)).c_str()) .value_or(DEFAULT_DISPLAY_SCALING); + display_exclusive_fullscreen_control = + ParseDisplayExclusiveFullscreenControl( + si.GetStringValue("Display", "ExclusiveFullscreenControl", + GetDisplayExclusiveFullscreenControlName(DEFAULT_DISPLAY_EXCLUSIVE_FULLSCREEN_CONTROL)) + .c_str()) + .value_or(DEFAULT_DISPLAY_EXCLUSIVE_FULLSCREEN_CONTROL); display_force_4_3_for_24bit = si.GetBoolValue("Display", "Force4_3For24Bit", false); display_active_start_offset = static_cast(si.GetIntValue("Display", "ActiveStartOffset", 0)); display_active_end_offset = static_cast(si.GetIntValue("Display", "ActiveEndOffset", 0)); @@ -475,6 +481,8 @@ void Settings::Save(SettingsInterface& si) const si.SetStringValue("Display", "AspectRatio", GetDisplayAspectRatioName(display_aspect_ratio)); si.SetStringValue("Display", "Alignment", GetDisplayAlignmentName(display_alignment)); si.SetStringValue("Display", "Scaling", GetDisplayScalingName(display_scaling)); + si.SetStringValue("Display", "ExclusiveFullscreenControl", + GetDisplayExclusiveFullscreenControlName(display_exclusive_fullscreen_control)); si.SetIntValue("Display", "CustomAspectRatioNumerator", display_aspect_ratio_custom_numerator); si.GetIntValue("Display", "CustomAspectRatioDenominator", display_aspect_ratio_custom_denominator); si.SetBoolValue("Display", "ShowOSDMessages", display_show_osd_messages); @@ -1238,6 +1246,42 @@ const char* Settings::GetDisplayScalingDisplayName(DisplayScalingMode mode) return Host::TranslateToCString("DisplayScalingMode", s_display_scaling_display_names[static_cast(mode)]); } +static constexpr const std::array s_display_exclusive_fullscreen_mode_names = { + "Automatic", + "Disallowed", + "Allowed", +}; +static constexpr const std::array s_display_exclusive_fullscreen_mode_display_names = { + TRANSLATE_NOOP("Settings", "Automatic (Default)"), + TRANSLATE_NOOP("Settings", "Disallowed"), + TRANSLATE_NOOP("Settings", "Allowed"), +}; + +std::optional Settings::ParseDisplayExclusiveFullscreenControl(const char* str) +{ + int index = 0; + for (const char* name : s_display_exclusive_fullscreen_mode_names) + { + if (StringUtil::Strcasecmp(name, str) == 0) + return static_cast(index); + + index++; + } + + return std::nullopt; +} + +const char* Settings::GetDisplayExclusiveFullscreenControlName(DisplayExclusiveFullscreenControl mode) +{ + return s_display_exclusive_fullscreen_mode_names[static_cast(mode)]; +} + +const char* Settings::GetDisplayExclusiveFullscreenControlDisplayName(DisplayExclusiveFullscreenControl mode) +{ + return Host::TranslateToCString("Settings", + s_display_exclusive_fullscreen_mode_display_names[static_cast(mode)]); +} + static constexpr const std::array s_audio_backend_names = { "Null", #ifdef ENABLE_CUBEB diff --git a/src/core/settings.h b/src/core/settings.h index 8a4eea8b1..c8d888c9d 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -124,6 +124,7 @@ struct Settings DisplayAspectRatio display_aspect_ratio = DEFAULT_DISPLAY_ASPECT_RATIO; DisplayAlignment display_alignment = DEFAULT_DISPLAY_ALIGNMENT; DisplayScalingMode display_scaling = DEFAULT_DISPLAY_SCALING; + DisplayExclusiveFullscreenControl display_exclusive_fullscreen_control = DEFAULT_DISPLAY_EXCLUSIVE_FULLSCREEN_CONTROL; u16 display_aspect_ratio_custom_numerator = 0; u16 display_aspect_ratio_custom_denominator = 0; s16 display_active_start_offset = 0; @@ -391,6 +392,10 @@ struct Settings static const char* GetDisplayScalingName(DisplayScalingMode mode); static const char* GetDisplayScalingDisplayName(DisplayScalingMode mode); + static std::optional ParseDisplayExclusiveFullscreenControl(const char* str); + static const char* GetDisplayExclusiveFullscreenControlName(DisplayExclusiveFullscreenControl mode); + static const char* GetDisplayExclusiveFullscreenControlDisplayName(DisplayExclusiveFullscreenControl mode); + static std::optional ParseAudioBackend(const char* str); static const char* GetAudioBackendName(AudioBackend backend); static const char* GetAudioBackendDisplayName(AudioBackend backend); @@ -450,6 +455,8 @@ struct Settings static constexpr DisplayAspectRatio DEFAULT_DISPLAY_ASPECT_RATIO = DisplayAspectRatio::Auto; static constexpr DisplayAlignment DEFAULT_DISPLAY_ALIGNMENT = DisplayAlignment::Center; static constexpr DisplayScalingMode DEFAULT_DISPLAY_SCALING = DisplayScalingMode::BilinearSmooth; + static constexpr DisplayExclusiveFullscreenControl DEFAULT_DISPLAY_EXCLUSIVE_FULLSCREEN_CONTROL = + DisplayExclusiveFullscreenControl::Automatic; static constexpr float DEFAULT_OSD_SCALE = 100.0f; static constexpr u8 DEFAULT_CDROM_READAHEAD_SECTORS = 8; diff --git a/src/core/system.cpp b/src/core/system.cpp index 773580a1b..7445af7a1 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -3509,12 +3509,14 @@ void System::SetCheatList(std::unique_ptr cheats) void System::CheckForSettingsChanges(const Settings& old_settings) { - if (IsValid() && (g_settings.gpu_renderer != old_settings.gpu_renderer || - g_settings.gpu_use_debug_device != old_settings.gpu_use_debug_device || - g_settings.gpu_threaded_presentation != old_settings.gpu_threaded_presentation || - g_settings.gpu_disable_shader_cache != old_settings.gpu_disable_shader_cache || - g_settings.gpu_disable_dual_source_blend != old_settings.gpu_disable_dual_source_blend || - g_settings.gpu_disable_framebuffer_fetch != old_settings.gpu_disable_framebuffer_fetch)) + if (IsValid() && + (g_settings.gpu_renderer != old_settings.gpu_renderer || + g_settings.gpu_use_debug_device != old_settings.gpu_use_debug_device || + g_settings.gpu_threaded_presentation != old_settings.gpu_threaded_presentation || + g_settings.gpu_disable_shader_cache != old_settings.gpu_disable_shader_cache || + g_settings.gpu_disable_dual_source_blend != old_settings.gpu_disable_dual_source_blend || + g_settings.gpu_disable_framebuffer_fetch != old_settings.gpu_disable_framebuffer_fetch || + g_settings.display_exclusive_fullscreen_control != old_settings.display_exclusive_fullscreen_control)) { // if debug device/threaded presentation change, we need to recreate the whole display const bool recreate_device = @@ -3522,7 +3524,8 @@ void System::CheckForSettingsChanges(const Settings& old_settings) g_settings.gpu_threaded_presentation != old_settings.gpu_threaded_presentation || g_settings.gpu_disable_shader_cache != old_settings.gpu_disable_shader_cache || g_settings.gpu_disable_dual_source_blend != old_settings.gpu_disable_dual_source_blend || - g_settings.gpu_disable_framebuffer_fetch != old_settings.gpu_disable_framebuffer_fetch); + g_settings.gpu_disable_framebuffer_fetch != old_settings.gpu_disable_framebuffer_fetch || + g_settings.display_exclusive_fullscreen_control != old_settings.display_exclusive_fullscreen_control); Host::AddIconOSDMessage("RendererSwitch", ICON_FA_PAINT_ROLLER, fmt::format(TRANSLATE_FS("OSDMessage", "Switching to {}{} GPU renderer."), diff --git a/src/core/types.h b/src/core/types.h index 320d647ea..d2096bcc6 100644 --- a/src/core/types.h +++ b/src/core/types.h @@ -143,6 +143,14 @@ enum class DisplayScalingMode : u8 Count }; +enum class DisplayExclusiveFullscreenControl : u8 +{ + Automatic, + Disallowed, + Allowed, + Count +}; + enum class AudioBackend : u8 { Null, diff --git a/src/duckstation-qt/advancedsettingswidget.cpp b/src/duckstation-qt/advancedsettingswidget.cpp index 51c41f71d..1ed739231 100644 --- a/src/duckstation-qt/advancedsettingswidget.cpp +++ b/src/duckstation-qt/advancedsettingswidget.cpp @@ -276,6 +276,11 @@ void AdvancedSettingsWidget::addTweakOptions() addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Apply Compatibility Settings"), "Main", "ApplyCompatibilitySettings", true); addIntRangeTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Display FPS Limit"), "Display", "MaxFPS", 0, 1000, 0); + addChoiceTweakOption( + m_dialog, m_ui.tweakOptionTable, tr("Exclusive Fullscreen Control"), "Display", "ExclusiveFullscreenControl", + &Settings::ParseDisplayExclusiveFullscreenControl, &Settings::GetDisplayExclusiveFullscreenControlName, + &Settings::GetDisplayExclusiveFullscreenControlDisplayName, + static_cast(DisplayExclusiveFullscreenControl::Count), Settings::DEFAULT_DISPLAY_EXCLUSIVE_FULLSCREEN_CONTROL); addMSAATweakOption(m_dialog, m_ui.tweakOptionTable, tr("Multisample Antialiasing")); @@ -377,6 +382,7 @@ void AdvancedSettingsWidget::onResetToDefaultClicked() setBooleanTweakOption(m_ui.tweakOptionTable, i++, false); // Show frame times setBooleanTweakOption(m_ui.tweakOptionTable, i++, true); // Apply compatibility settings setIntRangeTweakOption(m_ui.tweakOptionTable, i++, 0); // Display FPS limit + setChoiceTweakOption(m_ui.tweakOptionTable, i++, Settings::DEFAULT_DISPLAY_EXCLUSIVE_FULLSCREEN_CONTROL); setChoiceTweakOption(m_ui.tweakOptionTable, i++, 0); // Multisample antialiasing setChoiceTweakOption(m_ui.tweakOptionTable, i++, 0); // Wireframe mode setBooleanTweakOption(m_ui.tweakOptionTable, i++, false); // PGXP vertex cache @@ -433,6 +439,7 @@ void AdvancedSettingsWidget::onResetToDefaultClicked() sif->DeleteValue("Display", "LineStartOffset"); sif->DeleteValue("Display", "LineEndOffset"); sif->DeleteValue("Display", "StretchVertically"); + sif->DeleteValue("Display", "ExclusiveFullscreenControl"); sif->DeleteValue("GPU", "Multisamples"); sif->DeleteValue("GPU", "PerSampleShading"); sif->DeleteValue("GPU", "PGXPVertexCache"); diff --git a/src/util/d3d11_device.cpp b/src/util/d3d11_device.cpp index 2f6ae29ae..6502a6a4b 100644 --- a/src/util/d3d11_device.cpp +++ b/src/util/d3d11_device.cpp @@ -64,7 +64,7 @@ bool D3D11Device::HasSurface() const } bool D3D11Device::CreateDevice(const std::string_view& adapter, bool threaded_presentation, - FeatureMask disabled_features) + std::optional exclusive_fullscreen_control, FeatureMask disabled_features) { std::unique_lock lock(s_instance_mutex); diff --git a/src/util/d3d11_device.h b/src/util/d3d11_device.h index 900d5cbd1..d0f55b953 100644 --- a/src/util/d3d11_device.h +++ b/src/util/d3d11_device.h @@ -107,7 +107,7 @@ public: protected: bool CreateDevice(const std::string_view& adapter, bool threaded_presentation, - FeatureMask disabled_features) override; + std::optional exclusive_fullscreen_control, FeatureMask disabled_features) override; void DestroyDevice() override; private: diff --git a/src/util/d3d12_device.cpp b/src/util/d3d12_device.cpp index 2b64175b2..202488dc3 100644 --- a/src/util/d3d12_device.cpp +++ b/src/util/d3d12_device.cpp @@ -117,7 +117,7 @@ D3D12Device::ComPtr D3D12Device::CreateRootSignature(const } bool D3D12Device::CreateDevice(const std::string_view& adapter, bool threaded_presentation, - FeatureMask disabled_features) + std::optional exclusive_fullscreen_control, FeatureMask disabled_features) { std::unique_lock lock(s_instance_mutex); diff --git a/src/util/d3d12_device.h b/src/util/d3d12_device.h index 8040ae1ac..6eb6ed815 100644 --- a/src/util/d3d12_device.h +++ b/src/util/d3d12_device.h @@ -175,7 +175,7 @@ public: protected: bool CreateDevice(const std::string_view& adapter, bool threaded_presentation, - FeatureMask disabled_features) override; + std::optional exclusive_fullscreen_control, FeatureMask disabled_features) override; void DestroyDevice() override; bool ReadPipelineCache(const std::string& filename) override; diff --git a/src/util/gpu_device.cpp b/src/util/gpu_device.cpp index da949d1be..290a680c2 100644 --- a/src/util/gpu_device.cpp +++ b/src/util/gpu_device.cpp @@ -258,7 +258,7 @@ bool GPUDevice::IsSameRenderAPI(RenderAPI lhs, RenderAPI rhs) bool GPUDevice::Create(const std::string_view& adapter, const std::string_view& shader_cache_path, u32 shader_cache_version, bool debug_device, bool vsync, bool threaded_presentation, - FeatureMask disabled_features) + std::optional exclusive_fullscreen_control, FeatureMask disabled_features) { m_vsync_enabled = vsync; m_debug_device = debug_device; @@ -269,7 +269,7 @@ bool GPUDevice::Create(const std::string_view& adapter, const std::string_view& return false; } - if (!CreateDevice(adapter, threaded_presentation, disabled_features)) + if (!CreateDevice(adapter, threaded_presentation, exclusive_fullscreen_control, disabled_features)) { Log_ErrorPrintf("Failed to create device."); return false; diff --git a/src/util/gpu_device.h b/src/util/gpu_device.h index 76292ecdb..ad644243a 100644 --- a/src/util/gpu_device.h +++ b/src/util/gpu_device.h @@ -536,7 +536,8 @@ public: virtual RenderAPI GetRenderAPI() const = 0; bool Create(const std::string_view& adapter, const std::string_view& shader_cache_path, u32 shader_cache_version, - bool debug_device, bool vsync, bool threaded_presentation, FeatureMask disabled_features); + bool debug_device, bool vsync, bool threaded_presentation, + std::optional exclusive_fullscreen_control, FeatureMask disabled_features); void Destroy(); virtual bool HasSurface() const = 0; @@ -651,7 +652,7 @@ public: protected: virtual bool CreateDevice(const std::string_view& adapter, bool threaded_presentation, - FeatureMask disabled_features) = 0; + std::optional exclusive_fullscreen_control, FeatureMask disabled_features) = 0; virtual void DestroyDevice() = 0; std::string GetShaderCacheBaseName(const std::string_view& type) const; @@ -775,7 +776,8 @@ struct GLAutoPop #define GL_INS(msg) g_gpu_device->InsertDebugMessage(msg) #define GL_OBJECT_NAME(obj, name) (obj)->SetDebugName(name) -#define GL_SCOPE_FMT(...) GLAutoPop gl_auto_pop((g_gpu_device->PushDebugGroup(SmallString::from_format(__VA_ARGS__)), 0)) +#define GL_SCOPE_FMT(...) \ + GLAutoPop gl_auto_pop((g_gpu_device->PushDebugGroup(SmallString::from_format(__VA_ARGS__)), 0)) #define GL_PUSH_FMT(...) g_gpu_device->PushDebugGroup(SmallString::from_format(__VA_ARGS__)) #define GL_INS_FMT(...) g_gpu_device->InsertDebugMessage(SmallString::from_format(__VA_ARGS__)) #define GL_OBJECT_NAME_FMT(obj, ...) (obj)->SetDebugName(SmallString::from_format(__VA_ARGS__)) diff --git a/src/util/metal_device.h b/src/util/metal_device.h index 5b5ac32f3..181e942ed 100644 --- a/src/util/metal_device.h +++ b/src/util/metal_device.h @@ -257,7 +257,7 @@ public: protected: bool CreateDevice(const std::string_view& adapter, bool threaded_presentation, - FeatureMask disabled_features) override; + std::optional exclusive_fullscreen_control, FeatureMask disabled_features) override; void DestroyDevice() override; private: diff --git a/src/util/metal_device.mm b/src/util/metal_device.mm index 7be3ec508..676d3d6e5 100644 --- a/src/util/metal_device.mm +++ b/src/util/metal_device.mm @@ -121,6 +121,7 @@ void MetalDevice::SetVSync(bool enabled) } bool MetalDevice::CreateDevice(const std::string_view& adapter, bool threaded_presentation, + std::optional exclusive_fullscreen_control, FeatureMask disabled_features) { @autoreleasepool diff --git a/src/util/opengl_device.cpp b/src/util/opengl_device.cpp index 0c550947f..5ad769b80 100644 --- a/src/util/opengl_device.cpp +++ b/src/util/opengl_device.cpp @@ -307,7 +307,7 @@ bool OpenGLDevice::HasSurface() const } bool OpenGLDevice::CreateDevice(const std::string_view& adapter, bool threaded_presentation, - FeatureMask disabled_features) + std::optional exclusive_fullscreen_control, FeatureMask disabled_features) { m_gl_context = GL::Context::Create(m_window_info); if (!m_gl_context) diff --git a/src/util/opengl_device.h b/src/util/opengl_device.h index ef1abb1a4..afdb89546 100644 --- a/src/util/opengl_device.h +++ b/src/util/opengl_device.h @@ -120,7 +120,7 @@ public: protected: bool CreateDevice(const std::string_view& adapter, bool threaded_presentation, - FeatureMask disabled_features) override; + std::optional exclusive_fullscreen_control, FeatureMask disabled_features) override; void DestroyDevice() override; bool ReadPipelineCache(const std::string& filename) override; diff --git a/src/util/vulkan_device.cpp b/src/util/vulkan_device.cpp index 4202d3bac..c0fdfa471 100644 --- a/src/util/vulkan_device.cpp +++ b/src/util/vulkan_device.cpp @@ -1836,12 +1836,11 @@ bool VulkanDevice::HasSurface() const } bool VulkanDevice::CreateDevice(const std::string_view& adapter, bool threaded_presentation, - FeatureMask disabled_features) + std::optional exclusive_fullscreen_control, FeatureMask disabled_features) { std::unique_lock lock(s_instance_mutex); bool enable_debug_utils = m_debug_device; bool enable_validation_layer = m_debug_device; - std::optional exclusive_fullscreen_control; if (!Vulkan::LoadVulkanLibrary()) { @@ -1953,9 +1952,11 @@ bool VulkanDevice::CreateDevice(const std::string_view& adapter, bool threaded_p if (threaded_presentation) StartPresentThread(); + m_exclusive_fullscreen_control = exclusive_fullscreen_control; + if (surface != VK_NULL_HANDLE) { - m_swap_chain = VulkanSwapChain::Create(m_window_info, surface, m_vsync_enabled, exclusive_fullscreen_control); + m_swap_chain = VulkanSwapChain::Create(m_window_info, surface, m_vsync_enabled, m_exclusive_fullscreen_control); if (!m_swap_chain) { Log_ErrorPrintf("Failed to create swap chain"); @@ -2174,8 +2175,7 @@ bool VulkanDevice::UpdateWindow() return false; } - // TODO: exclusive fullscreen control - m_swap_chain = VulkanSwapChain::Create(m_window_info, surface, m_vsync_enabled, std::nullopt); + m_swap_chain = VulkanSwapChain::Create(m_window_info, surface, m_vsync_enabled, m_exclusive_fullscreen_control); if (!m_swap_chain) { Log_ErrorPrintf("Failed to create swap chain"); diff --git a/src/util/vulkan_device.h b/src/util/vulkan_device.h index ef0e11688..c693f76ad 100644 --- a/src/util/vulkan_device.h +++ b/src/util/vulkan_device.h @@ -218,7 +218,7 @@ public: protected: bool CreateDevice(const std::string_view& adapter, bool threaded_presentation, - FeatureMask disabled_features) override; + std::optional exclusive_fullscreen_control, FeatureMask disabled_features) override; void DestroyDevice() override; bool ReadPipelineCache(const std::string& filename) override; @@ -428,6 +428,7 @@ private: VkPhysicalDeviceProperties m_device_properties = {}; VkPhysicalDeviceDriverPropertiesKHR m_device_driver_properties = {}; OptionalExtensions m_optional_extensions = {}; + std::optional m_exclusive_fullscreen_control; std::unique_ptr m_swap_chain; std::unique_ptr m_null_texture;