mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-08 08:10:30 -10:00
video: add Exclusive Fullscreen Negotiation, and honour Forced on Vulkan
Windowed Fullscreen Mode off was supposed to mean exclusive fullscreen. On the Vulkan driver it reached the swapchain as VK_FULL_SCREEN_EXCLUSIVE_ALLOWED_EXT, which is a permission the driver may decline - and NVIDIA declines it. PresentMon on a 5090 reports "Hardware Composed: Independent Flip" with the setting in either state: the swapchain sits on DWM's multi-plane-overlay path regardless, and the toggle is inert. vkAcquireFullScreenExclusiveModeEXT was resolved in the vendored header and never called anywhere. Add a setting beside it. Relaxed is the current behaviour, ALLOWED and nothing more, and stays the default. Forced sets VK_FULL_SCREEN_EXCLUSIVE_APPLICATION_CONTROLLED_EXT on the surface and calls vkAcquireFullScreenExclusiveModeEXT once the swapchain exists. A failed acquire is a warning and the swapchain carries on where Relaxed would have left it. VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT - alt-tab, a mode switch - takes the same rebuild path as VK_ERROR_OUT_OF_DATE_KHR, and the mode is released before the swapchain it is bound to is destroyed. What Forced buys is not asserted here because it has not been measured: possibly lower present-to-scanout latency than the compositor path, genuinely immediate presentation that Scanline Sync could position against, and the application owning the display's HDR state rather than negotiating it through DWM. This is the switch that makes those measurable. The Windows-only pieces are under VK_USE_PLATFORM_WIN32_KHR. The setting exists on every platform and does nothing where the extension is absent. tools/settings_table.reference regenerated; the three Settings-Checks jobs pass locally.
This commit is contained in:
1 parent
c993b7ac34
commit
70e5889d9a
11 files changed
+119
-1
No files matched your search
@@ -264,6 +264,10 @@
|
||||
#define DEFAULT_WINDOWED_FULLSCREEN true
|
||||
#endif
|
||||
|
||||
/* Not platform-specific: how hard to push for exclusive fullscreen
|
||||
* where the platform lets the application decide. */
|
||||
#define DEFAULT_VIDEO_FSE_NEGOTIATION VIDEO_FSE_RELAXED
|
||||
|
||||
/* Enable automatic switching of the screen refresh rate when using the specified screen mode(s),
|
||||
* based on running core/content */
|
||||
#define DEFAULT_AUTOSWITCH_REFRESH_RATE AUTOSWITCH_REFRESH_RATE_EXCLUSIVE_FULLSCREEN
|
||||
|
||||
@@ -239,6 +239,7 @@ typedef struct settings
|
||||
unsigned video_viwidth;
|
||||
unsigned video_aspect_ratio_idx;
|
||||
unsigned video_rotation;
|
||||
unsigned video_fse_negotiation;
|
||||
unsigned screen_orientation;
|
||||
unsigned video_msg_bgcolor_red;
|
||||
unsigned video_msg_bgcolor_green;
|
||||
|
||||
@@ -1029,6 +1029,22 @@ static bool vulkan_context_init_device(gfx_ctx_vulkan_data_t *vk)
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef VK_USE_PLATFORM_WIN32_KHR
|
||||
/* Resolve the explicit acquire/release now the device exists. Only
|
||||
* used under VIDEO_FSE_FORCED; NULL otherwise and never called. */
|
||||
vk->fse_acquire = NULL;
|
||||
vk->fse_release = NULL;
|
||||
if (vk->fse_supported)
|
||||
{
|
||||
vk->fse_acquire = (PFN_vkAcquireFullScreenExclusiveModeEXT)
|
||||
vkGetDeviceProcAddr(vk->context.device,
|
||||
"vkAcquireFullScreenExclusiveModeEXT");
|
||||
vk->fse_release = (PFN_vkReleaseFullScreenExclusiveModeEXT)
|
||||
vkGetDeviceProcAddr(vk->context.device,
|
||||
"vkReleaseFullScreenExclusiveModeEXT");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VULKAN_HDR_SWAPCHAIN
|
||||
/* Now that the device exists, resolve vkSetHdrMetadataEXT if the
|
||||
* extension was enabled above. Stays NULL (call skipped) otherwise. */
|
||||
@@ -1470,6 +1486,12 @@ end:
|
||||
|
||||
static void vulkan_destroy_swapchain(gfx_ctx_vulkan_data_t *vk)
|
||||
{
|
||||
#ifdef VK_USE_PLATFORM_WIN32_KHR
|
||||
/* Exclusive mode is bound to the swapchain; release it first. */
|
||||
if (vk->fse_acquired && vk->fse_release && vk->swapchain != VK_NULL_HANDLE)
|
||||
vk->fse_release(vk->context.device, vk->swapchain);
|
||||
vk->fse_acquired = false;
|
||||
#endif
|
||||
unsigned i;
|
||||
|
||||
vulkan_emulated_mailbox_deinit(&vk->mailbox);
|
||||
@@ -2038,6 +2060,11 @@ retry:
|
||||
/* Do nothing. */
|
||||
break;
|
||||
case VK_ERROR_OUT_OF_DATE_KHR:
|
||||
#ifdef VK_USE_PLATFORM_WIN32_KHR
|
||||
case VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT:
|
||||
/* Alt-tab or a mode switch under Forced negotiation. Same
|
||||
* recovery as out-of-date: rebuild and re-acquire. */
|
||||
#endif
|
||||
/* Throw away the old swapchain and try again. */
|
||||
vulkan_destroy_swapchain(vk);
|
||||
/* Swapchain out of date, trying to create new one ... */
|
||||
@@ -2107,13 +2134,24 @@ bool vulkan_create_swapchain(gfx_ctx_vulkan_data_t *vk,
|
||||
bool adaptive_vsync = settings->bools.video_adaptive_vsync;
|
||||
#ifdef VK_USE_PLATFORM_WIN32_KHR
|
||||
bool video_windowed_fullscreen = settings->bools.video_windowed_fullscreen;
|
||||
/* Relaxed: ALLOWED is a hint and the driver may decline - and on
|
||||
* NVIDIA it does, leaving the swapchain on DWM's independent-flip
|
||||
* path with the setting silently inert (PresentMon reports
|
||||
* "Hardware Composed: Independent Flip" in both windowed-fullscreen
|
||||
* states). Forced: APPLICATION_CONTROLLED, followed by an explicit
|
||||
* vkAcquireFullScreenExclusiveModeEXT once the swapchain exists. */
|
||||
bool fse_forced =
|
||||
!video_windowed_fullscreen
|
||||
&& settings->uints.video_fse_negotiation == VIDEO_FSE_FORCED;
|
||||
HMONITOR hmonitor;
|
||||
VkSurfaceFullScreenExclusiveInfoEXT fse_info = {
|
||||
VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_INFO_EXT,
|
||||
NULL,
|
||||
video_windowed_fullscreen
|
||||
? VK_FULL_SCREEN_EXCLUSIVE_DISALLOWED_EXT
|
||||
: VK_FULL_SCREEN_EXCLUSIVE_ALLOWED_EXT
|
||||
: (fse_forced
|
||||
? VK_FULL_SCREEN_EXCLUSIVE_APPLICATION_CONTROLLED_EXT
|
||||
: VK_FULL_SCREEN_EXCLUSIVE_ALLOWED_EXT)
|
||||
};
|
||||
VkSurfaceFullScreenExclusiveWin32InfoEXT fse_win32_info = {
|
||||
VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_WIN32_INFO_EXT,
|
||||
@@ -2698,6 +2736,25 @@ bool vulkan_create_swapchain(gfx_ctx_vulkan_data_t *vk,
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef VK_USE_PLATFORM_WIN32_KHR
|
||||
vk->fse_acquired = false;
|
||||
if (fse_forced && vk->fse_supported && vk->fse_acquire)
|
||||
{
|
||||
VkResult res = vk->fse_acquire(vk->context.device, vk->swapchain);
|
||||
if (res == VK_SUCCESS)
|
||||
{
|
||||
vk->fse_acquired = true;
|
||||
RARCH_LOG("[Vulkan] Exclusive fullscreen acquired.\n");
|
||||
}
|
||||
else
|
||||
/* Not fatal: the swapchain still works on the compositor
|
||||
* path, which is where Relaxed would have left it anyway. */
|
||||
RARCH_WARN("[Vulkan] Exclusive fullscreen requested but "
|
||||
"vkAcquireFullScreenExclusiveModeEXT returned %d; "
|
||||
"continuing without it.\n", (int)res);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* See TODO/FIXME note above - part of the same rubber bandaid hack 'fix' */
|
||||
#ifndef __APPLE__
|
||||
if (old_swapchain != VK_NULL_HANDLE)
|
||||
|
||||
@@ -267,6 +267,14 @@ typedef struct gfx_ctx_vulkan_data
|
||||
uint8_t flags;
|
||||
enum vulkan_wsi_type wsi_type;
|
||||
bool fse_supported;
|
||||
/* Set once VK_FULL_SCREEN_EXCLUSIVE_APPLICATION_CONTROLLED_EXT has
|
||||
* been acquired on the current swapchain, so it is released before
|
||||
* that swapchain is destroyed. */
|
||||
bool fse_acquired;
|
||||
#ifdef VK_USE_PLATFORM_WIN32_KHR
|
||||
PFN_vkAcquireFullScreenExclusiveModeEXT fse_acquire;
|
||||
PFN_vkReleaseFullScreenExclusiveModeEXT fse_release;
|
||||
#endif
|
||||
#ifdef VULKAN_HDR_SWAPCHAIN
|
||||
/* Loaded from VK_EXT_hdr_metadata when that optional device extension is
|
||||
* present; NULL otherwise. Used to signal SMPTE-2086 mastering-display
|
||||
|
||||
@@ -102,6 +102,15 @@ enum video_rotation_type
|
||||
VIDEO_ROTATION_270_DEG
|
||||
};
|
||||
|
||||
/* How hard to push for exclusive fullscreen where the platform lets the
|
||||
* application decide (VK_EXT_full_screen_exclusive on Windows Vulkan). */
|
||||
enum video_fse_negotiation
|
||||
{
|
||||
VIDEO_FSE_RELAXED = 0, /* hint only; the driver may decline */
|
||||
VIDEO_FSE_FORCED, /* take it explicitly and hold it */
|
||||
VIDEO_FSE_LAST
|
||||
};
|
||||
|
||||
enum autoswitch_refresh_rate
|
||||
{
|
||||
AUTOSWITCH_REFRESH_RATE_EXCLUSIVE_FULLSCREEN = 0,
|
||||
|
||||
@@ -25350,6 +25350,14 @@ MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_SELECT_FILE,
|
||||
"Select File"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_VIDEO_FSE_RELAXED,
|
||||
"Relaxed"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_VIDEO_FSE_FORCED,
|
||||
"Forced"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_VIDEO_ROTATION_NORMAL,
|
||||
"Normal"
|
||||
|
||||
@@ -10625,6 +10625,7 @@ unsigned menu_displaylist_build_list(
|
||||
static menu_displaylist_build_info_selective_t build_list[] = {
|
||||
{MENU_ENUM_LABEL_VIDEO_FULLSCREEN, PARSE_ONLY_BOOL, true },
|
||||
{MENU_ENUM_LABEL_VIDEO_WINDOWED_FULLSCREEN, PARSE_ONLY_BOOL, true },
|
||||
{MENU_ENUM_LABEL_VIDEO_FSE_NEGOTIATION, PARSE_ONLY_UINT, true },
|
||||
{MENU_ENUM_LABEL_VIDEO_FULLSCREEN_X, PARSE_ONLY_UINT, true },
|
||||
{MENU_ENUM_LABEL_VIDEO_FULLSCREEN_Y, PARSE_ONLY_UINT, true },
|
||||
#ifdef __WINRT__
|
||||
|
||||
@@ -6981,6 +6981,26 @@ static size_t setting_get_string_representation_video_frame_delay(
|
||||
return _len;
|
||||
}
|
||||
|
||||
static size_t setting_get_string_representation_uint_video_fse_negotiation(
|
||||
rarch_setting_t *setting, char *s, size_t len)
|
||||
{
|
||||
if (setting)
|
||||
{
|
||||
switch (*setting->value.target.unsigned_integer)
|
||||
{
|
||||
case VIDEO_FSE_RELAXED:
|
||||
return strlcpy(s,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_FSE_RELAXED),
|
||||
len);
|
||||
case VIDEO_FSE_FORCED:
|
||||
return strlcpy(s,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_FSE_FORCED),
|
||||
len);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t setting_get_string_representation_uint_video_rotation(
|
||||
rarch_setting_t *setting, char *s, size_t len)
|
||||
{
|
||||
|
||||
@@ -8410,6 +8410,8 @@ enum msg_hash_enums
|
||||
MENU_LABEL(VIDEO_GAMMA),
|
||||
|
||||
MENU_LABEL(VIDEO_ALLOW_ROTATE),
|
||||
MENU_ENUM_LABEL_VALUE_VIDEO_FSE_RELAXED,
|
||||
MENU_ENUM_LABEL_VALUE_VIDEO_FSE_FORCED,
|
||||
MENU_ENUM_LABEL_VALUE_VIDEO_ROTATION_NORMAL,
|
||||
MENU_ENUM_LABEL_VALUE_VIDEO_ROTATION_90_DEG,
|
||||
MENU_ENUM_LABEL_VALUE_VIDEO_ROTATION_180_DEG,
|
||||
|
||||
@@ -14,6 +14,11 @@ S_BOOL(video_windowed_fullscreen, VIDEO_WINDOWED_FULLSCREEN,
|
||||
DEFAULT_WINDOWED_FULLSCREEN, SD_FLAG_LAKKA_ADVANCED, 0, CMD_EVENT_NONE,
|
||||
"Windowed Fullscreen Mode",
|
||||
"If fullscreen, prefer using a fullscreen window to prevent display mode switching.")
|
||||
S_UINT(video_fse_negotiation, VIDEO_FSE_NEGOTIATION,
|
||||
"video_fse_negotiation",
|
||||
DEFAULT_VIDEO_FSE_NEGOTIATION, SD_FLAG_LAKKA_ADVANCED, SDESC_RANGE_MINMAX, CMD_EVENT_REINIT, 0, VIDEO_FSE_LAST - 1, 1, 0, setting_action_ok_uint, setting_get_string_representation_uint_video_fse_negotiation,
|
||||
"Exclusive Fullscreen Negotiation",
|
||||
"How hard to ask the driver for exclusive fullscreen when Windowed Fullscreen Mode is off. 'Relaxed' only permits it and the driver may decline; on the Vulkan driver that is a hint the driver has been seen to ignore, leaving the swapchain on the compositor's independent-flip path. 'Forced' takes exclusive mode explicitly and holds it. Requires VK_EXT_full_screen_exclusive.")
|
||||
#if !defined(__WINRT__) || !defined(SETTINGS_DEF_CONFIG_PASS)
|
||||
S_UINT(video_fullscreen_x, VIDEO_FULLSCREEN_X,
|
||||
"video_fullscreen_x",
|
||||
|
||||
@@ -429,6 +429,7 @@ uint video_windowed_position_x 1 0
|
||||
uint video_windowed_position_y 1 0
|
||||
uint video_windowed_position_width 1 1280
|
||||
uint video_windowed_position_height 1 720
|
||||
uint video_fse_negotiation 1 VIDEO_FSE_RELAXED
|
||||
uint video_fullscreen_x 1 0
|
||||
uint video_fullscreen_y 1 0
|
||||
uint video_swap_interval 1 1
|
||||
@@ -1075,6 +1076,7 @@ uint video_windowed_position_x 1 0
|
||||
uint video_windowed_position_y 1 0
|
||||
uint video_windowed_position_width 1 1280
|
||||
uint video_windowed_position_height 1 720
|
||||
uint video_fse_negotiation 1 VIDEO_FSE_RELAXED
|
||||
uint video_fullscreen_x 1 0
|
||||
uint video_fullscreen_y 1 0
|
||||
uint video_swap_interval 1 1
|
||||
@@ -1724,6 +1726,7 @@ uint video_windowed_position_x 1 0
|
||||
uint video_windowed_position_y 1 0
|
||||
uint video_windowed_position_width 1 1280
|
||||
uint video_windowed_position_height 1 720
|
||||
uint video_fse_negotiation 1 VIDEO_FSE_RELAXED
|
||||
uint video_fullscreen_x 1 0
|
||||
uint video_fullscreen_y 1 0
|
||||
uint video_swap_interval 1 1
|
||||
|
||||
Reference in new issue
Block a user