Skip to content

android: recreate Vulkan surface after app resume#19216

Merged
LibretroAdmin merged 3 commits into
libretro:masterfrom
mattakins:fix-android-vulkan-surface-resume
Jul 26, 2026
Merged

android: recreate Vulkan surface after app resume#19216
LibretroAdmin merged 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.

@cscd98

cscd98 commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Tested on Android 16, with Dolphin and Vulkan, resuming RA now segfaults. Could you rebase on master, test and tell me which android version you are using?

07-26 12:37:03.136  6113  6113 I VRI[CocktailBarService]@c7396d1: Relayout returned: old=(1000,436,1080,778) new=(1000,436,1080,778) relayoutAsync=false req=(0,0)8 dur=2 res=0x2 s={false 0x0} ch=true seqId=17
07-26 12:37:03.140 21131 21131 F DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
07-26 12:37:03.140 21131 21131 F DEBUG   : Build fingerprint: 'samsung/dm2qxeea/dm2q:16/xx:user/release-keys'
07-26 12:37:03.140 21131 21131 F DEBUG   : Kernel Release: '5.15.189-android13-8-33413713-xx'
07-26 12:37:03.140 21131 21131 F DEBUG   : Revision: '13'
07-26 12:37:03.140 21131 21131 F DEBUG   : ABI: 'arm64'
07-26 12:37:03.140 21131 21131 F DEBUG   : Processor: '7'
07-26 12:37:03.140 21131 21131 F DEBUG   : Timestamp: 2026-07-26 12:37:02.908104182+0100
07-26 12:37:03.140 21131 21131 F DEBUG   : Process uptime: 20s
07-26 12:37:03.140 21131 21131 F DEBUG   : Executable: /system/bin/app_process64
07-26 12:37:03.140 21131 21131 F DEBUG   : Cmdline: com.retroarch.aarch64
07-26 12:37:03.140 21131 21131 F DEBUG   : pid: 20928, tid: 20955, name: Thread-2  >>> com.retroarch.aarch64 <<<
07-26 12:37:03.140 21131 21131 F DEBUG   : uid: 10518
07-26 12:37:03.140 21131 21131 F DEBUG   : tagged_addr_ctrl: 0000000000000001 (PR_TAGGED_ADDR_ENABLE)
07-26 12:37:03.140 21131 21131 F DEBUG   : pac_enabled_keys: 000000000000000f (PR_PAC_APIAKEY, PR_PAC_APIBKEY, PR_PAC_APDAKEY, PR_PAC_APDBKEY)
07-26 12:37:03.140 21131 21131 F DEBUG   : esr: 0000000092000006 (Data Abort Exception 0x24)
07-26 12:37:03.140 21131 21131 F DEBUG   : signal 11 (SIGSEGV), code -1 (SI_QUEUE), fault addr --------
07-26 12:37:03.140 21131 21131 F DEBUG   :     x0  0000007a4c289f20  x1  00000000000473a0  x2  000000000000000a  x3  0000004011807f20
07-26 12:37:03.140 21131 21131 F DEBUG   :     x4  00000000000473aa  x5  0000007a4c289f2a  x6  0000007ab3332ed4  x7  00000000ffffffb8
07-26 12:37:03.140 21131 21131 F DEBUG   :     x8  0000007a4c289f20  x9  9c9af4f51f163588  x10 0000000000000000  x11 b400007a5943b100
07-26 12:37:03.140 21131 21131 F DEBUG   :     x12 0000000000009fc8  x13 b400007abf261c80  x14 b400007a593a8cb0  x15 00000040117e0000
07-26 12:37:03.140 21131 21131 F DEBUG   :     x16 0000007ab3785528  x17 0000007ba18a7300  x18 0000007a61588000  x19 b400007a5943b100
07-26 12:37:03.140 21131 21131 F DEBUG   :     x20 0000000000000001  x21 00000000000473a0  x22 000000000000140b  x23 0000000000000005
07-26 12:37:03.140 21131 21131 F DEBUG   :     x24 0000000000000005  x25 0000000000015608  x26 0000004011807f20  x27 b400007a595ee0c0
07-26 12:37:03.140 21131 21131 F DEBUG   :     x28 000000000000000a  x29 0000007a65ff08a0
07-26 12:37:03.140 21131 21131 F DEBUG   :     lr  0000007ab34fd5a0  sp  0000007a65ff0670  pc  0000007ba18a7364  pst 0000000080001000
07-26 12:37:03.140 21131 21131 F DEBUG   :     esr 0000000092000006
07-26 12:37:03.140 21131 21131 F DEBUG   : 15 total frames
07-26 12:37:03.140 21131 21131 F DEBUG   : backtrace:
07-26 12:37:03.140 21131 21131 F DEBUG   :   NOTE: Function names and BuildId information is missing for some frames due
07-26 12:37:03.140 21131 21131 F DEBUG   :   NOTE: to unreadable libraries. For unwinds of apps, only shared libraries
07-26 12:37:03.140 21131 21131 F DEBUG   :   NOTE: found under the lib/ directory are readable.
07-26 12:37:03.140 21131 21131 F DEBUG   :   NOTE: On this device, run setenforce 0 to make the libraries readable.
07-26 12:37:03.140 21131 21131 F DEBUG   :   NOTE: Unreadable libraries:
07-26 12:37:03.140 21131 21131 F DEBUG   :   NOTE:   /data/data/com.retroarch.aarch64/cores/dolphin_libretro_android.so
07-26 12:37:03.140 21131 21131 F DEBUG   :       #00 pc 000000000009e364  /apex/com.android.runtime/lib64/bionic/libc.so (__memcpy_aarch64_simd+100) (BuildId: b0e5986f3545839dfcb13a827c175115)
07-26 12:37:03.140 21131 21131 F DEBUG   :       #01 pc 00000000002be59c  /vendor/lib64/egl/libGLESv2_adreno.so (!!!0000!77df12deb6a622478efa8fb9929034!3df4c357f0!+4748) (BuildId: d2badd9449737bd110fdcb0b2f6dda4e)
07-26 12:37:03.140 21131 21131 F DEBUG   :       #02 pc 00000000008df1fc  /data/data/com.retroarch.aarch64/cores/dolphin_libretro_android.so
07-26 12:37:03.140 21131 21131 F DEBUG   :       #03 pc 00000000008ddc38  /data/data/com.retroarch.aarch64/cores/dolphin_libretro_android.so
07-26 12:37:03.140 21131 21131 F DEBUG   :       #04 pc 000000000091a128  /data/data/com.retroarch.aarch64/cores/dolphin_libretro_android.so
07-26 12:37:03.140 21131 21131 F DEBUG   :       #05 pc 0000000000852608  /data/data/com.retroarch.aarch64/cores/dolphin_libretro_android.so
07-26 12:37:03.140 21131 21131 F DEBUG   :       #06 pc 000000000083155c  /data/data/com.retroarch.aarch64/cores/dolphin_libretro_android.so
07-26 12:37:03.140 21131 21131 F DEBUG   :       #07 pc 000000000053781c  /data/data/com.retroarch.aarch64/cores/dolphin_libretro_android.so
07-26 12:37:03.140 21131 21131 F DEBUG   :       #08 pc 0000000000af27e8  /data/app/~~hxKqPDYgX2NNhtCJzaQZyQ==/com.retroarch.aarch64-yJehhCKvoFFm8uf4So3vPQ==/lib/arm64/libretroarch-activity.so (core_run+284) (BuildId: 45d1b01bbf16543d9dfdbcca2f65bf7c1cecc6a5)
07-26 12:37:03.140 21131 21131 F DEBUG   :       #09 pc 0000000000ae1af8  /data/app/~~hxKqPDYgX2NNhtCJzaQZyQ==/com.retroarch.aarch64-yJehhCKvoFFm8uf4So3vPQ==/lib/arm64/libretroarch-activity.so (runloop_iterate+1604) (BuildId: 45d1b01bbf16543d9dfdbcca2f65bf7c1cecc6a5)
07-26 12:37:03.140 21131 21131 F DEBUG   :       #10 pc 0000000000ae1090  /data/app/~~hxKqPDYgX2NNhtCJzaQZyQ==/com.retroarch.aarch64-yJehhCKvoFFm8uf4So3vPQ==/lib/arm64/libretroarch-activity.so (rarch_main+868) (BuildId: 45d1b01bbf16543d9dfdbcca2f65bf7c1cecc6a5)
07-26 12:37:03.140 21131 21131 F DEBUG   :       #11 pc 0000000000ce5364  /data/app/~~hxKqPDYgX2NNhtCJzaQZyQ==/com.retroarch.aarch64-yJehhCKvoFFm8uf4So3vPQ==/lib/arm64/libretroarch-activity.so (BuildId: 45d1b01bbf16543d9dfdbcca2f65bf7c1cecc6a5)
07-26 12:37:03.140 21131 21131 F DEBUG   :       #12 pc 0000000000af9ce4  /data/app/~~hxKqPDYgX2NNhtCJzaQZyQ==/com.retroarch.aarch64-yJehhCKvoFFm8uf4So3vPQ==/lib/arm64/libretroarch-activity.so (BuildId: 45d1b01bbf16543d9dfdbcca2f65bf7c1cecc6a5)
07-26 12:37:03.140 21131 21131 F DEBUG   :       #13 pc 00000000000ba178  /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*) (.__uniq.67847048707805468364044055584648682506)+184) (BuildId: b0e5986f3545839dfcb13a827c175115)
07-26 12:37:03.140 21131 21131 F DEBUG   :       #14 pc 00000000000ace0c  /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+68) (BuildId: b0e5986f3545839dfcb13a827c175115)

@cscd98

cscd98 commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Actually, I now can't reproduce the crash I had.. so lets go!

@LibretroAdmin
LibretroAdmin merged commit 3624ab4 into libretro:master Jul 26, 2026
43 checks passed
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.

5 participants