Skip to content

android: recreate Vulkan surface after app resume#19216

Open
mattakins wants to merge 3 commits into
libretro:masterfrom
mattakins:fix-android-vulkan-surface-resume
Open

android: recreate Vulkan surface after app resume#19216
mattakins wants to merge 3 commits into
libretro:masterfrom
mattakins:fix-android-vulkan-surface-resume

Conversation

@mattakins

@mattakins mattakins commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Problem

On Android, RetroArch could resume Vulkan rendering after the Android window lifecycle had invalidated the presentation surface or swapchain.

Android may retain the same ANativeWindow while the app is backgrounded, so waiting only for a later window teardown could leave Vulkan presentation resources alive too long. This could stall the presentation queue or lead to a resume crash or freeze. User may notice a black flash before game resumes.

Fix

Handle Android Vulkan surface loss independently of the Vulkan device:

  • Destroy the swapchain and VkSurfaceKHR when the app stops.
  • Keep the Vulkan device, shader state, textures, menu state, and input state.
  • Recreate the surface on resume, verify the existing graphics queue can present to it, and recreate the swapchain.
  • Avoid presenting while no Android surface is available.
  • Rebuild driver-owned swapchain resources before rendering after a context-side swapchain recreation.
  • Validate frame/image indices and avoid beginning a render pass with a null framebuffer.
  • Wait for Android to provide a new native window before attempting surface recreation.
  • Drain threaded video before destroying the presentation surface, without holding the Android lifecycle mutex.
  • Reset the current swapchain image index when replacing a swapchain, so an index from a larger old swapchain cannot be used with a smaller new one.

If surface recreation fails, the existing full-context recovery path remains available.

Validation

Tested on Android with Vulkan:

  • Repeated background → resume cycles with shaders enabled and disabled.
  • Resume with the menu both open and closed.
  • Shader disable/re-enable after resume.
  • Toggled threaded video off while using Vulkan; verified recovery from the prior invalid-swapchain-index hang.
  • Tested Dolphin with Vulkan hardware rendering through repeated background/resume cycles.
  • Tested a software-rendered core with Vulkan, threaded video, and a shader.

No resume crash or permanent presentation stall was observed in the tested paths. Eliminates black flash before game resumes after backgrounding.

@mattakins
mattakins marked this pull request as ready for review July 16, 2026 20:36

@white-axe white-axe left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should probably change the parts of these comments that say "Currently only for OpenGL" now that these fields are used for Android Vulkan as well:

/* 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. */
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. */
bool (*destroy_surface)(void *data);

Also change this comment to mention Vulkan as well, if applicable:

if (runloop_flags & RUNLOOP_FLAG_PAUSED)
{
/* When using OpenGL, pausing the app (e.g. by opening the app switcher)
* will result in the EGL window surface being destroyed, but the actual
* OpenGL context will be preserved on most devices, so we may be able to
* get away with reinitializing only the window surface without having to
* do a full video driver reinitialization. */
video_driver_state_t *state = video_state_get_ptr();
if (state->current_video_context.create_surface == NULL || !state->current_video_context.create_surface(state->context_data))
command_event(CMD_EVENT_REINIT, NULL);
}

@white-axe

Copy link
Copy Markdown
Contributor

Also, did you make sure this works for cores that actually use Vulkan for hardware-accelerated rendering via the RETRO_ENVIRONMENT_SET_HW_RENDER libretro environment call with the RETRO_HW_CONTEXT_VULKAN hardware context type, as opposed to only cores that use software rendering? I added a largely equivalent feature for Android OpenGL in #18484 but couldn't figure out how to do the same for Vulkan without breaking existing hardware-accelerated cores, but it's likely I just wasn't knowledgeable enough about Vulkan.

Wait for an Android window before scheduling Vulkan surface recreation,
and drain threaded video before destroying a surface. Surface destruction
runs outside the Android mutex so the video worker can finish safely.

Reset the current swapchain image index when replacing a swapchain so an
index from a larger old swapchain cannot survive into a smaller new one.
@mattakins

mattakins commented Jul 19, 2026

Copy link
Copy Markdown
Contributor Author

@white-axe Great callouts. I updated the comments to describe surface-only recreation rather than marking them as OpenGL-only.

I also tested this with a Vulkan hardware-rendered core, Dolphin. The logs confirm RETRO_HW_CONTEXT_VULKAN, SET_HW_RENDER, Vulkan context negotiation, and the Dolphin Vulkan backend. I ran repeated background → resume cycles without a crash or hang.

For completeness, RetroArch disables threaded video for Vulkan hardware-render cores, so I tested both relevant paths:

  • Dolphin with Vulkan hardware rendering, shader, no shader.
  • Gambatte with Vulkan, threaded video,shader, no shader.
  • Gambatte with Vulkan, threaded video off, shader, no shader.

All passed repeated resume testing.

Also this actually led to a more robust fix here. I updated the PR description.

@hizzlekizzle @LibretroAdmin This should be good to go.

@mattakins
mattakins requested a review from white-axe July 19, 2026 17:29

@white-axe white-axe left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me! I tried it with PPSSPP with its Vulkan renderer selected as well and it works nicely.

@hizzlekizzle

Copy link
Copy Markdown
Collaborator

This looks great. Thanks for the testing and iteration!

We have another guy who's been focused on getting the Play Store version updated, and that's been taking most of his time, we're hoping he can get eyes on this soon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants