Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 57 additions & 4 deletions gfx/common/vulkan_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -1403,6 +1403,23 @@ static void vulkan_destroy_swapchain(gfx_ctx_vulkan_data_t *vk)
vk->context.num_recycled_acquire_semaphores = 0;
}

bool vulkan_surface_destroy(gfx_ctx_vulkan_data_t *vk)
{
if (!vk || !vk->context.instance)
return false;

vulkan_destroy_swapchain(vk);

if (vk->vk_surface != VK_NULL_HANDLE)
{
vkDestroySurfaceKHR(vk->context.instance,
vk->vk_surface, NULL);
vk->vk_surface = VK_NULL_HANDLE;
}

return true;
}

static void vulkan_acquire_clear_fences(gfx_ctx_vulkan_data_t *vk)
{
unsigned i;
Expand Down Expand Up @@ -1696,16 +1713,48 @@ bool vulkan_surface_create(gfx_ctx_vulkan_data_t *vk,
return false;
}

/* Must create device after surface since we need to be able to query queues to use for presentation. */
if (!vulkan_context_init_device(vk))
return false;
/* Must create device after surface since we need to be able to query queues
* to use for presentation. When replacing a lost surface, retain the
* existing device and verify that its queue can present to the new one. */
if (vk->context.device == VK_NULL_HANDLE)
{
if (!vulkan_context_init_device(vk))
goto error_surface;
}
else
{
VkResult res;
VkBool32 supported = VK_FALSE;

res = vkGetPhysicalDeviceSurfaceSupportKHR(
vk->context.gpu,
vk->context.graphics_queue_index,
vk->vk_surface, &supported);
if (res != VK_SUCCESS || !supported)
{
RARCH_ERR("[Vulkan] Existing queue cannot present to replacement surface (err = %d).\n",
(int)res);
goto error_surface;
}
}

if (!vulkan_create_swapchain(
vk, width, height, swap_interval))
return false;
goto error_swapchain;

vulkan_acquire_next_image(vk);
return true;

error_swapchain:
vulkan_destroy_swapchain(vk);
error_surface:
if (vk->vk_surface != VK_NULL_HANDLE)
{
vkDestroySurfaceKHR(vk->context.instance,
vk->vk_surface, NULL);
vk->vk_surface = VK_NULL_HANDLE;
}
return false;
}

uint32_t vulkan_find_memory_type(
Expand Down Expand Up @@ -2587,6 +2636,10 @@ bool vulkan_create_swapchain(gfx_ctx_vulkan_data_t *vk,
/* Force driver to reset swapchain image handles. */
vk->context.flags |= VK_CTX_FLAG_INVALID_SWAPCHAIN;
vk->context.flags &= ~VK_CTX_FLAG_HAS_ACQUIRED_SWAPCHAIN;
/* A replacement swapchain can have fewer images than its predecessor.
* Do not retain an image index from the old swapchain while waiting for
* the first acquire from the new one. */
vk->context.current_swapchain_index = 0;
vulkan_create_wait_fences(vk);

if (vk->flags & VK_DATA_FLAG_EMULATING_MAILBOX)
Expand Down
2 changes: 2 additions & 0 deletions gfx/common/vulkan_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,8 @@ bool vulkan_surface_create(gfx_ctx_vulkan_data_t *vk,
unsigned width, unsigned height,
int8_t swap_interval);

bool vulkan_surface_destroy(gfx_ctx_vulkan_data_t *vk);

void vulkan_present(gfx_ctx_vulkan_data_t *vk, unsigned index);

void vulkan_acquire_next_image(gfx_ctx_vulkan_data_t *vk);
Expand Down
54 changes: 50 additions & 4 deletions gfx/drivers/vulkan.c
Original file line number Diff line number Diff line change
Expand Up @@ -5216,6 +5216,9 @@ static void *vulkan_init(const video_info_t *video,
#endif

vulkan_init_readback(vk, settings->bools.video_gpu_record);

/* Driver resources now match the context's current swapchain. */
vk->context->flags &= ~VK_CTX_FLAG_INVALID_SWAPCHAIN;
return vk;

error:
Expand Down Expand Up @@ -5349,6 +5352,25 @@ static void vulkan_check_swapchain(vk_t *vk)
RARCH_ERR("[Vulkan] Failed to update filter chain info.\n");
}

static bool vulkan_recreate_context_swapchain(vk_t *vk)
{
unsigned width = vk->context->swapchain_width;
unsigned height = vk->context->swapchain_height;

if (!vk->ctx_driver->set_resize)
return false;

/* set_resize is the context-owned recreation path. It replaces the
* swapchain before returning, releasing any acquired image that cannot be
* submitted safely by this frame. */
vk->context->flags |= VK_CTX_FLAG_INVALID_SWAPCHAIN;
if (!vk->ctx_driver->set_resize(vk->ctx_data, width, height))
return false;

vulkan_check_swapchain(vk);
return true;
}

static void vulkan_set_nonblock_state(void *data, bool state,
bool adaptive_vsync_enabled,
unsigned swap_interval)
Expand Down Expand Up @@ -6360,12 +6382,27 @@ static bool vulkan_frame(void *data, const void *frame,
#ifdef HAVE_GFX_WIDGETS
bool widgets_active = video_info->widgets_active;
#endif
unsigned frame_index =
vk->context->current_frame_index;
unsigned swapchain_index =
vk->context->current_swapchain_index;
unsigned frame_index;
unsigned swapchain_index;
bool overlay_behind_menu = video_info->overlay_behind_menu;

/* The context may recreate its swapchain while acquiring the next
* image. Rebuild driver-owned framebuffers before recording commands
* against images from the new swapchain. */
if (vk->context->flags & VK_CTX_FLAG_INVALID_SWAPCHAIN)
vulkan_check_swapchain(vk);

frame_index = vk->context->current_frame_index;
swapchain_index = vk->context->current_swapchain_index;

if ( frame_index >= vk->num_swapchain_images
|| swapchain_index >= vk->num_swapchain_images)
{
RARCH_ERR("[Vulkan] Invalid swapchain indices (frame %u, image %u, count %u).\n",
frame_index, swapchain_index, vk->num_swapchain_images);
return vulkan_recreate_context_swapchain(vk);
}

/* Fast toggle shader filter chain logic */
filter_chain = vk->filter_chain;

Expand Down Expand Up @@ -6748,8 +6785,17 @@ static bool vulkan_frame(void *data, const void *frame,
backbuffer = &vk->offscreen_buffer;
#endif /* VULKAN_HDR_SWAPCHAIN */

if ( (backbuffer->image != VK_NULL_HANDLE)
&& (backbuffer->framebuffer == VK_NULL_HANDLE))
{
RARCH_ERR("[Vulkan] Swapchain image %u has no framebuffer.\n",
swapchain_index);
return vulkan_recreate_context_swapchain(vk);
}

/* Render to backbuffer. */
if ( (backbuffer->image != VK_NULL_HANDLE)
&& (backbuffer->framebuffer != VK_NULL_HANDLE)
&& (vk->context->flags & VK_CTX_FLAG_HAS_ACQUIRED_SWAPCHAIN))
{
rp_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
Expand Down
58 changes: 56 additions & 2 deletions gfx/drivers_context/android_vk_ctx.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ typedef struct
unsigned width;
unsigned height;
int swap_interval;
bool surface_lost;
} android_ctx_data_vk_t;

/* FORWARD DECLARATION */
Expand Down Expand Up @@ -168,11 +169,60 @@ static bool android_gfx_ctx_vk_set_video_mode(void *data,
RARCH_ERR("[Vulkan] Failed to create surface.\n");
return false;
}
and->surface_lost = false;
RARCH_LOG("[Vulkan] Native window size: %ux%u.\n",
and->width, and->height);
return true;
}

static bool android_gfx_ctx_vk_create_surface(void *data)
{
struct android_app *android_app = (struct android_app*)g_android;
android_ctx_data_vk_t *and = (android_ctx_data_vk_t*)data;

/* APP_CMD_INIT_WINDOW can remain pending after startup even though the
* Vulkan surface is already active. */
if (and && !and->surface_lost
&& and->vk.vk_surface != VK_NULL_HANDLE)
{
RARCH_LOG("[Vulkan] Ignoring duplicate Android window initialization.\n");
return true;
}

if (!android_app || !android_app->window || !and)
return false;

and->width = ANativeWindow_getWidth(android_app->window);
and->height = ANativeWindow_getHeight(android_app->window);

if (!vulkan_surface_create(&and->vk, VULKAN_WSI_ANDROID,
NULL, android_app->window,
and->width, and->height, and->swap_interval))
{
RARCH_ERR("[Vulkan] Failed to recreate Android surface.\n");
return false;
}

and->surface_lost = false;
RARCH_LOG("[Vulkan] Recreated Android surface: %ux%u.\n",
and->width, and->height);
return true;
}

static bool android_gfx_ctx_vk_destroy_surface(void *data)
{
android_ctx_data_vk_t *and = (android_ctx_data_vk_t*)data;

if (!and)
return false;

and->surface_lost = true;
if (!vulkan_surface_destroy(&and->vk))
return false;

return true;
}

static void android_gfx_ctx_vk_input_driver(void *data,
const char *joypad_name,
input_driver_t **input, void **input_data)
Expand Down Expand Up @@ -201,6 +251,10 @@ static void android_gfx_ctx_vk_swap_buffers(void *data)
{
android_ctx_data_vk_t *and = (android_ctx_data_vk_t*)data;

if (!and || and->surface_lost
|| and->vk.vk_surface == VK_NULL_HANDLE)
return;

if (and->vk.context.flags & VK_CTX_FLAG_HAS_ACQUIRED_SWAPCHAIN)
{
and->vk.context.flags &= ~VK_CTX_FLAG_HAS_ACQUIRED_SWAPCHAIN;
Expand Down Expand Up @@ -281,6 +335,6 @@ const gfx_ctx_driver_t gfx_ctx_vk_android = {
android_gfx_ctx_vk_bind_hw_render,
android_gfx_ctx_vk_get_context_data,
NULL, /* make_current */
NULL, /* create_surface */
NULL /* destroy_surface */
android_gfx_ctx_vk_create_surface,
android_gfx_ctx_vk_destroy_surface
};
12 changes: 6 additions & 6 deletions gfx/video_driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -584,14 +584,14 @@ typedef struct gfx_ctx_driver
* active for this thread. */
void (*make_current)(bool release);

/* Optional. Creates and binds a new window surface, destroying the original
* window surface if applicable. Returns true on success and false on error.
* Currently only for OpenGL. */
/* Optional. Creates and binds a replacement window surface without
* reinitializing the underlying graphics context/device. Returns true on
* success and false on error. */
bool (*create_surface)(void *data);

/* Optional. Destroys the current window surface. Returns true on success or
* or if there is no currently bound window surface and false on error.
* Currently only for OpenGL. */
/* Optional. Destroys the current window surface without reinitializing the
* underlying graphics context/device. Returns true on success, or if no
* window surface is bound, and false on error. */
bool (*destroy_surface)(void *data);
} gfx_ctx_driver_t;

Expand Down
Loading
Loading