GS/Vulkan: Add exclusive fullscreen control option

This commit is contained in:
Stenzek 2023-04-30 01:35:04 +10:00 committed by refractionpcsx2
parent 3e631e047f
commit 6736ef1d44
10 changed files with 120 additions and 30 deletions

View File

@ -373,6 +373,11 @@ namespace Vulkan
m_optional_extensions.vk_khr_shader_draw_parameters =
SupportsExtension(VK_KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME, false);
#ifdef _WIN32
m_optional_extensions.vk_ext_full_screen_exclusive =
SupportsExtension(VK_EXT_FULL_SCREEN_EXCLUSIVE_EXTENSION_NAME, false);
#endif
return true;
}

View File

@ -54,6 +54,7 @@ namespace Vulkan
bool vk_ext_calibrated_timestamps : 1;
bool vk_ext_line_rasterization : 1;
bool vk_ext_rasterization_order_attachment_access : 1;
bool vk_ext_full_screen_exclusive : 1;
bool vk_khr_driver_properties : 1;
bool vk_khr_fragment_shader_barycentric : 1;
bool vk_khr_shader_draw_parameters : 1;

View File

@ -29,10 +29,12 @@
namespace Vulkan
{
SwapChain::SwapChain(const WindowInfo& wi, VkSurfaceKHR surface, VkPresentModeKHR preferred_present_mode)
SwapChain::SwapChain(const WindowInfo& wi, VkSurfaceKHR surface, VkPresentModeKHR preferred_present_mode,
std::optional<bool> exclusive_fullscreen_control)
: m_window_info(wi)
, m_surface(surface)
, m_preferred_present_mode(preferred_present_mode)
, m_exclusive_fullscreen_control(exclusive_fullscreen_control)
{
}
@ -412,9 +414,10 @@ namespace Vulkan
}
std::unique_ptr<SwapChain> SwapChain::Create(const WindowInfo& wi, VkSurfaceKHR surface,
VkPresentModeKHR preferred_present_mode)
VkPresentModeKHR preferred_present_mode, std::optional<bool> exclusive_fullscreen_control)
{
std::unique_ptr<SwapChain> swap_chain = std::make_unique<SwapChain>(wi, surface, preferred_present_mode);
std::unique_ptr<SwapChain> swap_chain = std::unique_ptr<SwapChain>(
new SwapChain(wi, surface, preferred_present_mode, exclusive_fullscreen_control));
if (!swap_chain->CreateSwapChain() || !swap_chain->SetupSwapChainImages())
return nullptr;
@ -577,6 +580,7 @@ namespace Vulkan
}
// Store the old/current swap chain when recreating for resize
// Old swap chain is destroyed regardless of whether the create call succeeds
VkSwapchainKHR old_swap_chain = m_swap_chain;
m_swap_chain = VK_NULL_HANDLE;
@ -596,10 +600,27 @@ namespace Vulkan
swap_chain_info.pQueueFamilyIndices = indices.data();
}
if (m_swap_chain == VK_NULL_HANDLE)
#ifdef _WIN32
VkSurfaceFullScreenExclusiveInfoEXT exclusive_info = {VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_INFO_EXT};
if (g_vulkan_context->GetOptionalExtensions().vk_ext_full_screen_exclusive)
{
res = vkCreateSwapchainKHR(g_vulkan_context->GetDevice(), &swap_chain_info, nullptr, &m_swap_chain);
exclusive_info.fullScreenExclusive = m_exclusive_fullscreen_control.has_value() ?
(m_exclusive_fullscreen_control.value() ?
VK_FULL_SCREEN_EXCLUSIVE_ALLOWED_EXT :
VK_FULL_SCREEN_EXCLUSIVE_DISALLOWED_EXT) :
VK_FULL_SCREEN_EXCLUSIVE_DEFAULT_EXT;
Util::AddPointerToChain(&swap_chain_info, &exclusive_info);
}
else if (m_exclusive_fullscreen_control.has_value())
{
Console.Error("Exclusive fullscreen control requested, but VK_EXT_full_screen_exclusive is not supported.");
}
#else
if (m_exclusive_fullscreen_control.has_value())
Console.Error("Exclusive fullscreen control requested, but is not supported on this platform.");
#endif
res = vkCreateSwapchainKHR(g_vulkan_context->GetDevice(), &swap_chain_info, nullptr, &m_swap_chain);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkCreateSwapchainKHR failed: ");

View File

@ -28,7 +28,6 @@ namespace Vulkan
class SwapChain
{
public:
SwapChain(const WindowInfo& wi, VkSurfaceKHR surface, VkPresentModeKHR preferred_present_mode);
~SwapChain();
// Creates a vulkan-renderable surface for the specified window handle.
@ -49,7 +48,7 @@ namespace Vulkan
// Create a new swap chain from a pre-existing surface.
static std::unique_ptr<SwapChain> Create(const WindowInfo& wi, VkSurfaceKHR surface,
VkPresentModeKHR preferred_present_mode);
VkPresentModeKHR preferred_present_mode, std::optional<bool> exclusive_fullscreen_control);
__fi VkSurfaceKHR GetSurface() const { return m_surface; }
__fi VkSurfaceFormatKHR GetSurfaceFormat() const { return m_surface_format; }
@ -91,6 +90,9 @@ namespace Vulkan
}
private:
SwapChain(const WindowInfo& wi, VkSurfaceKHR surface, VkPresentModeKHR preferred_present_mode,
std::optional<bool> exclusive_fullscreen_control);
bool SelectSurfaceFormat();
bool SelectPresentMode();
@ -131,5 +133,6 @@ namespace Vulkan
u32 m_current_image = 0;
u32 m_current_semaphore = 0;
std::optional<VkResult> m_image_acquire_result;
std::optional<bool> m_exclusive_fullscreen_control;
};
} // namespace Vulkan

View File

@ -181,7 +181,8 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
//////////////////////////////////////////////////////////////////////////
// HW Renderer Fixes
//////////////////////////////////////////////////////////////////////////
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.crcFixLevel, "EmuCore/GS", "crc_hack_level", static_cast<int>(CRCHackLevel::Automatic), -1);
SettingWidgetBinder::BindWidgetToIntSetting(
sif, m_ui.crcFixLevel, "EmuCore/GS", "crc_hack_level", static_cast<int>(CRCHackLevel::Automatic), -1);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.halfScreenFix, "EmuCore/GS", "UserHacks_Half_Bottom_Override", -1, -1);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.cpuSpriteRenderBW, "EmuCore/GS", "UserHacks_CPUSpriteRenderBW", 0);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.cpuSpriteRenderLevel, "EmuCore/GS", "UserHacks_CPUSpriteRenderLevel", 0);
@ -199,13 +200,14 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
SettingWidgetBinder::BindWidgetToIntSetting(
sif, m_ui.textureInsideRt, "EmuCore/GS", "UserHacks_TextureInsideRt", static_cast<int>(GSTextureInRtMode::Disabled));
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.readTCOnClose, "EmuCore/GS", "UserHacks_ReadTCOnClose", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.targetPartialInvalidation, "EmuCore/GS", "UserHacks_TargetPartialInvalidation", false);
SettingWidgetBinder::BindWidgetToBoolSetting(
sif, m_ui.targetPartialInvalidation, "EmuCore/GS", "UserHacks_TargetPartialInvalidation", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.estimateTextureRegion, "EmuCore/GS", "UserHacks_EstimateTextureRegion", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.gpuPaletteConversion, "EmuCore/GS", "paltex", false);
connect(m_ui.cpuSpriteRenderBW, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&GraphicsSettingsWidget::onCPUSpriteRenderBWChanged);
connect(m_ui.textureInsideRt, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&GraphicsSettingsWidget::onTextureInsideRtChanged);
connect(
m_ui.textureInsideRt, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &GraphicsSettingsWidget::onTextureInsideRtChanged);
connect(m_ui.gpuPaletteConversion, QOverload<int>::of(&QCheckBox::stateChanged), this,
&GraphicsSettingsWidget::onGpuPaletteConversionChanged);
onCPUSpriteRenderBWChanged();
@ -244,6 +246,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.useDebugDevice, "EmuCore/GS", "UseDebugDevice", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.skipPresentingDuplicateFrames, "EmuCore/GS", "SkipDuplicateFrames", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.threadedPresentation, "EmuCore/GS", "DisableThreadedPresentation", false);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.exclusiveFullscreenControl, "EmuCore/GS", "ExclusiveFullscreenControl", -1, -1);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.overrideTextureBarriers, "EmuCore/GS", "OverrideTextureBarriers", -1, -1);
SettingWidgetBinder::BindWidgetToIntSetting(
sif, m_ui.gsDumpCompression, "EmuCore/GS", "GSDumpCompression", static_cast<int>(GSDumpCompressionMethod::Zstandard));
@ -295,6 +298,12 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
connect(m_ui.swTextureFiltering, &QComboBox::currentIndexChanged, this, &GraphicsSettingsWidget::onSWTextureFilteringChange);
updateRendererDependentOptions();
#ifndef _WIN32
// Exclusive fullscreen control is Windows-only.
m_ui.advancedOptionsFormLayout->removeRow(2);
m_ui.exclusiveFullscreenControl = nullptr;
#endif
#ifndef PCSX2_DEVBUILD
if (!m_dialog->isPerGameSettings())
{
@ -384,8 +393,8 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
dialog->registerWidgetHelp(m_ui.PCRTCOverscan, tr("Show Overscan"), tr("Unchecked"),
tr("Enables the option to show the overscan area on games which draw more than the safe area of the screen."));
dialog->registerWidgetHelp(m_ui.fmvAspectRatio, tr("FMV Aspect Ratio"), tr("Off (Default)"),
tr("Overrides the full-motion video (FMV) aspect ratio."));
dialog->registerWidgetHelp(
m_ui.fmvAspectRatio, tr("FMV Aspect Ratio"), tr("Off (Default)"), tr("Overrides the full-motion video (FMV) aspect ratio."));
dialog->registerWidgetHelp(m_ui.PCRTCAntiBlur, tr("Anti-Blur"), tr("Checked"),
tr("Enables internal Anti-Blur hacks. Less accurate to PS2 rendering but will make a lot of games look less blurry."));
@ -422,17 +431,17 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
dialog->registerWidgetHelp(m_ui.fullscreenModes, tr("Fullscreen Mode"), tr("Borderless Fullscreen"),
tr("Chooses the fullscreen resolution and frequency."));
dialog->registerWidgetHelp(m_ui.cropLeft, tr("Left"), tr("0px"),
tr("Changes the number of pixels cropped from the left side of the display."));
dialog->registerWidgetHelp(
m_ui.cropLeft, tr("Left"), tr("0px"), tr("Changes the number of pixels cropped from the left side of the display."));
dialog->registerWidgetHelp(m_ui.cropTop, tr("Top"), tr("0px"),
tr("Changes the number of pixels cropped from the top of the display."));
dialog->registerWidgetHelp(
m_ui.cropTop, tr("Top"), tr("0px"), tr("Changes the number of pixels cropped from the top of the display."));
dialog->registerWidgetHelp(m_ui.cropRight, tr("Right"), tr("0px"),
tr("Changes the number of pixels cropped from the right side of the display."));
dialog->registerWidgetHelp(
m_ui.cropRight, tr("Right"), tr("0px"), tr("Changes the number of pixels cropped from the right side of the display."));
dialog->registerWidgetHelp(m_ui.cropBottom, tr("Bottom"), tr("0px"),
tr("Changes the number of pixels cropped from the bottom of the display."));
dialog->registerWidgetHelp(
m_ui.cropBottom, tr("Bottom"), tr("0px"), tr("Changes the number of pixels cropped from the bottom of the display."));
}
// Rendering tab
@ -639,8 +648,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
// OSD tab
{
dialog->registerWidgetHelp(
m_ui.osdScale, tr("OSD Scale"), tr("100%"), tr("Scales the size of the onscreen OSD from 50% to 500%."));
dialog->registerWidgetHelp(m_ui.osdScale, tr("OSD Scale"), tr("100%"), tr("Scales the size of the onscreen OSD from 50% to 500%."));
dialog->registerWidgetHelp(m_ui.osdShowMessages, tr("Show OSD Messages"), tr("Checked"),
tr("Shows on-screen-display messages when events occur such as save states being "
@ -702,6 +710,10 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
"renderer. This usually results in slower performance, but may be required for some "
"streaming applications, or to uncap framerates on some systems."));
dialog->registerWidgetHelp(m_ui.exclusiveFullscreenControl, tr("Allow Exclusive Fullscreen"), tr("Automatic (Default)"),
tr("Overrides the driver's heuristics for enabling exclusive fullscreen, or direct flip/scanout.<br>"
"Disallowing exclusive fullscreen may enable smoother task switching and overlays, but increase input latency."));
dialog->registerWidgetHelp(m_ui.useDebugDevice, tr("Use Debug Device"), tr("Unchecked"), tr(""));
dialog->registerWidgetHelp(m_ui.disableDualSource, tr("Disable Dual Source Blending"), tr("Unchecked"), tr(""));
@ -710,10 +722,9 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
dialog->registerWidgetHelp(m_ui.skipPresentingDuplicateFrames, tr("Skip Presenting Duplicate Frames"), tr("Unchecked"),
tr("Detects when idle frames are being presented in 25/30fps games, and skips presenting those frames. The frame is still "
"rendered, it just means "
"the GPU has more time to complete it (this is NOT frame skipping). Can smooth our frame time fluctuations when the CPU/GPU "
"are near maximum "
"utilization, but makes frame pacing more inconsistent and can increase input lag."));
"rendered, it just means the GPU has more time to complete it (this is NOT frame skipping). Can smooth our frame time "
"fluctuations when the CPU/GPU are near maximum utilization, but makes frame pacing more inconsistent and can increase "
"input lag."));
dialog->registerWidgetHelp(m_ui.threadedPresentation, tr("Disable Threaded Presentation"), tr("Unchecked"),
tr("Presents frames on the main GS thread instead of a worker thread. Used for debugging frametime issues. "
@ -911,6 +922,8 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
const bool is_hardware = (type == GSRendererType::DX11 || type == GSRendererType::DX12 || type == GSRendererType::OGL ||
type == GSRendererType::VK || type == GSRendererType::Metal);
const bool is_software = (type == GSRendererType::SW);
const bool is_auto = (type == GSRendererType::Auto);
const bool is_vk = (type == GSRendererType::VK);
const bool hw_fixes = (is_hardware && m_ui.enableHWFixes && m_ui.enableHWFixes->checkState() == Qt::Checked);
const int prev_tab = m_ui.tabs->currentIndex();
@ -946,6 +959,8 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
m_ui.disableFramebufferFetch->setDisabled(is_sw_dx);
m_ui.exclusiveFullscreenControl->setEnabled(is_auto || is_vk);
// populate adapters
std::vector<std::string> adapters;
std::vector<std::string> fullscreen_modes;

View File

@ -2117,6 +2117,32 @@
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_48">
<property name="text">
<string>Allow Exclusive Fullscreen:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="exclusiveFullscreenControl">
<item>
<property name="text">
<string>Automatic (Default)</string>
</property>
</item>
<item>
<property name="text">
<string>Disallowed</string>
</property>
</item>
<item>
<property name="text">
<string>Allowed</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>

View File

@ -19,6 +19,7 @@
#include "common/General.h"
#include <array>
#include <string>
#include <optional>
#include <vector>
class SettingsInterface;
@ -626,6 +627,9 @@ struct Pcsx2Config
static const char* GetRendererName(GSRendererType type);
/// Converts a tri-state option to an optional boolean value.
static std::optional<bool> TriStateToOptionalBoolean(int value);
static constexpr float DEFAULT_FRAME_RATE_NTSC = 59.94f;
static constexpr float DEFAULT_FRAME_RATE_PAL = 50.00f;
@ -778,6 +782,7 @@ struct Pcsx2Config
int SaveN = 0;
int SaveL = 5000;
s8 ExclusiveFullscreenControl = -1;
GSScreenshotSize ScreenshotSize = GSScreenshotSize::WindowResolution;
GSScreenshotFormat ScreenshotFormat = GSScreenshotFormat::PNG;
int ScreenshotQuality = 50;

View File

@ -3325,6 +3325,10 @@ void FullscreenUI::DrawGraphicsSettingsPage()
DrawIntListSetting(bsi, "Hardware Download Mode", "Changes synchronization behavior for GS downloads.", "EmuCore/GS",
"HWDownloadMode", static_cast<int>(GSHardwareDownloadMode::Enabled), s_hw_download, std::size(s_hw_download));
}
DrawIntListSetting(bsi, "Allow Exclusive Fullscreen",
"Overrides the driver's heuristics for enabling exclusive fullscreen, or direct flip/scanout.", "EmuCore/GS",
"ExclusiveFullscreenControl", -1, s_generic_options, std::size(s_generic_options), -1,
(renderer == GSRendererType::Auto || renderer == GSRendererType::VK));
DrawIntListSetting(bsi, "Override Texture Barriers", "Forces texture barrier functionality to the specified value.", "EmuCore/GS",
"OverrideTextureBarriers", -1, s_generic_options, std::size(s_generic_options), -1);
DrawIntListSetting(bsi, "GS Dump Compression", "Sets the compression algorithm for GS dumps.", "EmuCore/GS", "GSDumpCompression",

View File

@ -306,7 +306,8 @@ bool GSDeviceVK::UpdateWindow()
return false;
}
m_swap_chain = Vulkan::SwapChain::Create(m_window_info, surface, GetPreferredPresentModeForVsyncMode(m_vsync_mode));
m_swap_chain = Vulkan::SwapChain::Create(m_window_info, surface, GetPreferredPresentModeForVsyncMode(m_vsync_mode),
Pcsx2Config::GSOptions::TriStateToOptionalBoolean(GSConfig.ExclusiveFullscreenControl));
if (!m_swap_chain)
{
Console.Error("Failed to create swap chain");
@ -659,7 +660,8 @@ bool GSDeviceVK::CreateDeviceAndSwapChain()
if (surface != VK_NULL_HANDLE)
{
m_swap_chain =
Vulkan::SwapChain::Create(m_window_info, surface, GetPreferredPresentModeForVsyncMode(m_vsync_mode));
Vulkan::SwapChain::Create(m_window_info, surface, GetPreferredPresentModeForVsyncMode(m_vsync_mode),
Pcsx2Config::GSOptions::TriStateToOptionalBoolean(GSConfig.ExclusiveFullscreenControl));
if (!m_swap_chain)
{
Console.Error("Failed to create swap chain");

View File

@ -395,6 +395,11 @@ const char* Pcsx2Config::GSOptions::GetRendererName(GSRendererType type)
}
}
std::optional<bool> Pcsx2Config::GSOptions::TriStateToOptionalBoolean(int value)
{
return (value < 0) ? std::optional<bool>(std::nullopt) : std::optional<bool>((value != 0));
}
Pcsx2Config::GSOptions::GSOptions()
{
bitset = 0;
@ -539,6 +544,7 @@ bool Pcsx2Config::GSOptions::OptionsAreEqual(const GSOptions& right) const
OpEqu(SaveN) &&
OpEqu(SaveL) &&
OpEqu(ExclusiveFullscreenControl) &&
OpEqu(ScreenshotSize) &&
OpEqu(ScreenshotFormat) &&
OpEqu(ScreenshotQuality) &&
@ -574,7 +580,8 @@ bool Pcsx2Config::GSOptions::RestartOptionsAreEqual(const GSOptions& right) cons
OpEqu(DisableDualSourceBlend) &&
OpEqu(DisableFramebufferFetch) &&
OpEqu(DisableThreadedPresentation) &&
OpEqu(OverrideTextureBarriers);
OpEqu(OverrideTextureBarriers) &&
OpEqu(ExclusiveFullscreenControl);
}
void Pcsx2Config::GSOptions::LoadSave(SettingsWrapper& wrap)
@ -727,6 +734,7 @@ void Pcsx2Config::GSOptions::LoadSave(SettingsWrapper& wrap)
GSSettingInt(ShadeBoost_Brightness);
GSSettingInt(ShadeBoost_Contrast);
GSSettingInt(ShadeBoost_Saturation);
GSSettingInt(ExclusiveFullscreenControl);
GSSettingIntEx(PNGCompressionLevel, "png_compression_level");
GSSettingIntEx(SaveN, "saven");
GSSettingIntEx(SaveL, "savel");