mirror of
https://github.com/libretro/RetroArch
synced 2025-03-26 02:37:23 +00:00
Emulate mailbox by wrapping acquire in a thread.
Needed on nVidia Windows since they don't support timeout == 0 ...
This commit is contained in:
parent
157d1e7e63
commit
d39f66a738
@ -42,9 +42,7 @@
|
|||||||
#define VENDOR_ID_NV 0x10DE
|
#define VENDOR_ID_NV 0x10DE
|
||||||
#define VENDOR_ID_INTEL 0x8086
|
#define VENDOR_ID_INTEL 0x8086
|
||||||
|
|
||||||
// Windows is not particularly good at recreating swapchains.
|
#ifdef _WIN32
|
||||||
// Emulate vsync toggling by using vkAcquireNextImageKHR timeouts.
|
|
||||||
#if defined(_WIN32)
|
|
||||||
#define VULKAN_EMULATE_MAILBOX
|
#define VULKAN_EMULATE_MAILBOX
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -118,6 +116,126 @@ static VKAPI_ATTR VkBool32 VKAPI_CALL vulkan_debug_cb(
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void vulkan_emulated_mailbox_deinit(struct vulkan_emulated_mailbox *mailbox)
|
||||||
|
{
|
||||||
|
if (mailbox->thread)
|
||||||
|
{
|
||||||
|
slock_lock(mailbox->lock);
|
||||||
|
mailbox->dead = true;
|
||||||
|
scond_signal(mailbox->cond);
|
||||||
|
slock_unlock(mailbox->lock);
|
||||||
|
sthread_join(mailbox->thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mailbox->lock)
|
||||||
|
slock_free(mailbox->lock);
|
||||||
|
if (mailbox->cond)
|
||||||
|
scond_free(mailbox->cond);
|
||||||
|
|
||||||
|
memset(mailbox, 0, sizeof(*mailbox));
|
||||||
|
}
|
||||||
|
|
||||||
|
VkResult vulkan_emulated_mailbox_acquire_next_image(struct vulkan_emulated_mailbox *mailbox,
|
||||||
|
unsigned *index)
|
||||||
|
{
|
||||||
|
VkResult res;
|
||||||
|
if (mailbox->swapchain == VK_NULL_HANDLE)
|
||||||
|
return VK_ERROR_OUT_OF_DATE_KHR;
|
||||||
|
|
||||||
|
slock_lock(mailbox->lock);
|
||||||
|
|
||||||
|
if (!mailbox->has_pending_request)
|
||||||
|
{
|
||||||
|
mailbox->request_acquire = true;
|
||||||
|
mailbox->has_pending_request = true;
|
||||||
|
scond_signal(mailbox->cond);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mailbox->acquired)
|
||||||
|
{
|
||||||
|
/* Wait some arbitrary time here for good measure.
|
||||||
|
* This lets us grab the index from mailbox_begin_acquire early. */
|
||||||
|
scond_wait_timeout(mailbox->cond, mailbox->lock, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mailbox->acquired)
|
||||||
|
{
|
||||||
|
res = mailbox->result;
|
||||||
|
*index = mailbox->index;
|
||||||
|
mailbox->has_pending_request = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
res = VK_TIMEOUT;
|
||||||
|
|
||||||
|
mailbox->acquired = false;
|
||||||
|
slock_unlock(mailbox->lock);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vulkan_emulated_mailbox_loop(void *userdata)
|
||||||
|
{
|
||||||
|
VkResult res;
|
||||||
|
VkFence fence;
|
||||||
|
VkFenceCreateInfo info = { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO };
|
||||||
|
struct vulkan_emulated_mailbox *mailbox =
|
||||||
|
(struct vulkan_emulated_mailbox *)userdata;
|
||||||
|
|
||||||
|
vkCreateFence(mailbox->device, &info, NULL, &fence);
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
slock_lock(mailbox->lock);
|
||||||
|
while (!mailbox->dead && !mailbox->request_acquire)
|
||||||
|
scond_wait(mailbox->cond, mailbox->lock);
|
||||||
|
|
||||||
|
if (mailbox->dead)
|
||||||
|
{
|
||||||
|
slock_unlock(mailbox->lock);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
mailbox->request_acquire = false;
|
||||||
|
slock_unlock(mailbox->lock);
|
||||||
|
|
||||||
|
mailbox->result = vkAcquireNextImageKHR(mailbox->device, mailbox->swapchain, UINT64_MAX,
|
||||||
|
VK_NULL_HANDLE, fence, &mailbox->index);
|
||||||
|
|
||||||
|
if (mailbox->result == VK_SUCCESS)
|
||||||
|
vkWaitForFences(mailbox->device, 1, &fence, true, UINT64_MAX);
|
||||||
|
vkResetFences(mailbox->device, 1, &fence);
|
||||||
|
|
||||||
|
if (mailbox->result == VK_SUCCESS)
|
||||||
|
{
|
||||||
|
slock_lock(mailbox->lock);
|
||||||
|
mailbox->acquired = true;
|
||||||
|
scond_signal(mailbox->cond);
|
||||||
|
slock_unlock(mailbox->lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vkDestroyFence(mailbox->device, fence, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool vulkan_emulated_mailbox_init(struct vulkan_emulated_mailbox *mailbox,
|
||||||
|
VkDevice device,
|
||||||
|
VkSwapchainKHR swapchain)
|
||||||
|
{
|
||||||
|
memset(mailbox, 0, sizeof(*mailbox));
|
||||||
|
mailbox->device = device;
|
||||||
|
mailbox->swapchain = swapchain;
|
||||||
|
|
||||||
|
mailbox->cond = scond_new();
|
||||||
|
if (!mailbox->cond)
|
||||||
|
return false;
|
||||||
|
mailbox->lock = slock_new();
|
||||||
|
if (!mailbox->lock)
|
||||||
|
return false;
|
||||||
|
mailbox->thread = sthread_create(vulkan_emulated_mailbox_loop, mailbox);
|
||||||
|
if (!mailbox->thread)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t vulkan_find_memory_type(
|
uint32_t vulkan_find_memory_type(
|
||||||
const VkPhysicalDeviceMemoryProperties *mem_props,
|
const VkPhysicalDeviceMemoryProperties *mem_props,
|
||||||
uint32_t device_reqs, uint32_t host_reqs)
|
uint32_t device_reqs, uint32_t host_reqs)
|
||||||
@ -1579,10 +1697,13 @@ static bool vulkan_context_init_device(gfx_ctx_vulkan_data_t *vk)
|
|||||||
&vk->context.memory_properties);
|
&vk->context.memory_properties);
|
||||||
|
|
||||||
#ifdef VULKAN_EMULATE_MAILBOX
|
#ifdef VULKAN_EMULATE_MAILBOX
|
||||||
|
/*
|
||||||
// AMD can emulate Mailbox on Windows, but not NV.
|
// AMD can emulate Mailbox on Windows, but not NV.
|
||||||
// Not tested on Intel.
|
// Not tested on Intel.
|
||||||
if (vk->context.gpu_properties.vendorID == VENDOR_ID_AMD)
|
if (vk->context.gpu_properties.vendorID == VENDOR_ID_AMD)
|
||||||
vk->emulate_mailbox = true;
|
vk->emulate_mailbox = true;
|
||||||
|
*/
|
||||||
|
vk->emulate_mailbox = true;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
RARCH_LOG("[Vulkan]: Using GPU: %s\n", vk->context.gpu_properties.deviceName);
|
RARCH_LOG("[Vulkan]: Using GPU: %s\n", vk->context.gpu_properties.deviceName);
|
||||||
@ -2344,6 +2465,7 @@ static void vulkan_destroy_swapchain(gfx_ctx_vulkan_data_t *vk)
|
|||||||
{
|
{
|
||||||
unsigned i;
|
unsigned i;
|
||||||
|
|
||||||
|
vulkan_emulated_mailbox_deinit(&vk->mailbox);
|
||||||
if (vk->swapchain != VK_NULL_HANDLE)
|
if (vk->swapchain != VK_NULL_HANDLE)
|
||||||
{
|
{
|
||||||
vkDeviceWaitIdle(vk->context.device);
|
vkDeviceWaitIdle(vk->context.device);
|
||||||
@ -2545,19 +2667,17 @@ retry:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vkCreateFence(vk->context.device, &fence_info, NULL, &fence);
|
|
||||||
|
|
||||||
if (vk->emulating_mailbox)
|
if (vk->emulating_mailbox)
|
||||||
{
|
{
|
||||||
/* Non-blocking acquire. If we don't get a swapchain frame right away,
|
/* Non-blocking acquire. If we don't get a swapchain frame right away,
|
||||||
* just skip rendering to the swapchain this frame, similar to what
|
* just skip rendering to the swapchain this frame, similar to what
|
||||||
* MAILBOX would do. */
|
* MAILBOX would do. */
|
||||||
err = vkAcquireNextImageKHR(vk->context.device,
|
err = vulkan_emulated_mailbox_acquire_next_image(&vk->mailbox, &vk->context.current_swapchain_index);
|
||||||
vk->swapchain, 0,
|
fence = VK_NULL_HANDLE;
|
||||||
VK_NULL_HANDLE, fence, &vk->context.current_swapchain_index);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
vkCreateFence(vk->context.device, &fence_info, NULL, &fence);
|
||||||
err = vkAcquireNextImageKHR(vk->context.device,
|
err = vkAcquireNextImageKHR(vk->context.device,
|
||||||
vk->swapchain, UINT64_MAX,
|
vk->swapchain, UINT64_MAX,
|
||||||
VK_NULL_HANDLE, fence, &vk->context.current_swapchain_index);
|
VK_NULL_HANDLE, fence, &vk->context.current_swapchain_index);
|
||||||
@ -2565,6 +2685,7 @@ retry:
|
|||||||
|
|
||||||
if (err == VK_SUCCESS)
|
if (err == VK_SUCCESS)
|
||||||
{
|
{
|
||||||
|
if (fence != VK_NULL_HANDLE)
|
||||||
vkWaitForFences(vk->context.device, 1, &fence, true, UINT64_MAX);
|
vkWaitForFences(vk->context.device, 1, &fence, true, UINT64_MAX);
|
||||||
vk->context.has_acquired_swapchain = true;
|
vk->context.has_acquired_swapchain = true;
|
||||||
}
|
}
|
||||||
@ -2575,6 +2696,7 @@ retry:
|
|||||||
trigger_spurious_error_vkresult(&err);
|
trigger_spurious_error_vkresult(&err);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (fence != VK_NULL_HANDLE)
|
||||||
vkDestroyFence(vk->context.device, fence, NULL);
|
vkDestroyFence(vk->context.device, fence, NULL);
|
||||||
|
|
||||||
if (err == VK_NOT_READY || err == VK_TIMEOUT)
|
if (err == VK_NOT_READY || err == VK_TIMEOUT)
|
||||||
@ -2665,9 +2787,16 @@ bool vulkan_create_swapchain(gfx_ctx_vulkan_data_t *vk,
|
|||||||
RARCH_LOG("[Vulkan]: Do not need to re-create swapchain.\n");
|
RARCH_LOG("[Vulkan]: Do not need to re-create swapchain.\n");
|
||||||
vk->created_new_swapchain = false;
|
vk->created_new_swapchain = false;
|
||||||
vulkan_create_wait_fences(vk);
|
vulkan_create_wait_fences(vk);
|
||||||
|
|
||||||
|
if (vk->emulating_mailbox && vk->mailbox.swapchain == VK_NULL_HANDLE)
|
||||||
|
vulkan_emulated_mailbox_init(&vk->mailbox, vk->context.device, vk->swapchain);
|
||||||
|
else if (!vk->emulating_mailbox && vk->mailbox.swapchain != VK_NULL_HANDLE)
|
||||||
|
vulkan_emulated_mailbox_deinit(&vk->mailbox);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vulkan_emulated_mailbox_deinit(&vk->mailbox);
|
||||||
|
|
||||||
present_mode_count = 0;
|
present_mode_count = 0;
|
||||||
vkGetPhysicalDeviceSurfacePresentModesKHR(
|
vkGetPhysicalDeviceSurfacePresentModesKHR(
|
||||||
vk->context.gpu, vk->vk_surface,
|
vk->context.gpu, vk->vk_surface,
|
||||||
@ -2903,5 +3032,9 @@ bool vulkan_create_swapchain(gfx_ctx_vulkan_data_t *vk,
|
|||||||
/* Force driver to reset swapchain image handles. */
|
/* Force driver to reset swapchain image handles. */
|
||||||
vk->context.invalid_swapchain = true;
|
vk->context.invalid_swapchain = true;
|
||||||
vulkan_create_wait_fences(vk);
|
vulkan_create_wait_fences(vk);
|
||||||
|
|
||||||
|
if (vk->emulating_mailbox)
|
||||||
|
vulkan_emulated_mailbox_init(&vk->mailbox, vk->context.device, vk->swapchain);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -128,6 +128,27 @@ typedef struct vulkan_context
|
|||||||
#endif
|
#endif
|
||||||
} vulkan_context_t;
|
} vulkan_context_t;
|
||||||
|
|
||||||
|
struct vulkan_emulated_mailbox
|
||||||
|
{
|
||||||
|
sthread_t *thread;
|
||||||
|
VkDevice device;
|
||||||
|
VkSwapchainKHR swapchain;
|
||||||
|
slock_t *lock;
|
||||||
|
scond_t *cond;
|
||||||
|
|
||||||
|
unsigned index;
|
||||||
|
bool acquired;
|
||||||
|
bool request_acquire;
|
||||||
|
bool dead;
|
||||||
|
bool has_pending_request;
|
||||||
|
VkResult result;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool vulkan_emulated_mailbox_init(struct vulkan_emulated_mailbox *mailbox,
|
||||||
|
VkDevice device, VkSwapchainKHR swapchain);
|
||||||
|
void vulkan_emulated_mailbox_deinit(struct vulkan_emulated_mailbox *mailbox);
|
||||||
|
VkResult vulkan_emulated_mailbox_acquire_next_image(struct vulkan_emulated_mailbox *mailbox, unsigned *index);
|
||||||
|
|
||||||
typedef struct gfx_ctx_vulkan_data
|
typedef struct gfx_ctx_vulkan_data
|
||||||
{
|
{
|
||||||
bool need_new_swapchain;
|
bool need_new_swapchain;
|
||||||
@ -137,6 +158,8 @@ typedef struct gfx_ctx_vulkan_data
|
|||||||
vulkan_context_t context;
|
vulkan_context_t context;
|
||||||
VkSurfaceKHR vk_surface;
|
VkSurfaceKHR vk_surface;
|
||||||
VkSwapchainKHR swapchain;
|
VkSwapchainKHR swapchain;
|
||||||
|
|
||||||
|
struct vulkan_emulated_mailbox mailbox;
|
||||||
} gfx_ctx_vulkan_data_t;
|
} gfx_ctx_vulkan_data_t;
|
||||||
|
|
||||||
struct vulkan_display_surface_info
|
struct vulkan_display_surface_info
|
||||||
|
@ -1906,7 +1906,8 @@ static bool vulkan_frame(void *data, const void *frame,
|
|||||||
|
|
||||||
vk->readback.pending = false;
|
vk->readback.pending = false;
|
||||||
}
|
}
|
||||||
else if (chain->backbuffer.image != VK_NULL_HANDLE)
|
else if (chain->backbuffer.image != VK_NULL_HANDLE &&
|
||||||
|
vk->context->has_acquired_swapchain)
|
||||||
{
|
{
|
||||||
/* Prepare backbuffer for presentation. */
|
/* Prepare backbuffer for presentation. */
|
||||||
vulkan_image_layout_transition(vk, vk->cmd,
|
vulkan_image_layout_transition(vk, vk->cmd,
|
||||||
@ -1966,8 +1967,11 @@ static bool vulkan_frame(void *data, const void *frame,
|
|||||||
|
|
||||||
submit_info.signalSemaphoreCount = 0;
|
submit_info.signalSemaphoreCount = 0;
|
||||||
|
|
||||||
if (vk->context->swapchain_semaphores[frame_index] != VK_NULL_HANDLE)
|
if (vk->context->swapchain_semaphores[frame_index] != VK_NULL_HANDLE &&
|
||||||
|
vk->context->has_acquired_swapchain)
|
||||||
|
{
|
||||||
signal_semaphores[submit_info.signalSemaphoreCount++] = vk->context->swapchain_semaphores[frame_index];
|
signal_semaphores[submit_info.signalSemaphoreCount++] = vk->context->swapchain_semaphores[frame_index];
|
||||||
|
}
|
||||||
|
|
||||||
if (vk->hw.signal_semaphore != VK_NULL_HANDLE)
|
if (vk->hw.signal_semaphore != VK_NULL_HANDLE)
|
||||||
{
|
{
|
||||||
@ -2009,7 +2013,9 @@ static bool vulkan_frame(void *data, const void *frame,
|
|||||||
/* Disable BFI during fast forward, slow-motion,
|
/* Disable BFI during fast forward, slow-motion,
|
||||||
* and pause to prevent flicker. */
|
* and pause to prevent flicker. */
|
||||||
if (
|
if (
|
||||||
video_info->black_frame_insertion
|
chain->backbuffer.image != VK_NULL_HANDLE
|
||||||
|
&& vk->context->has_acquired_swapchain
|
||||||
|
&& video_info->black_frame_insertion
|
||||||
&& !video_info->input_driver_nonblock_state
|
&& !video_info->input_driver_nonblock_state
|
||||||
&& !video_info->runloop_is_slowmotion
|
&& !video_info->runloop_is_slowmotion
|
||||||
&& !video_info->runloop_is_paused)
|
&& !video_info->runloop_is_paused)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user