From 27119a264e9a24cd6b652e68b775eb64f1c3dd82 Mon Sep 17 00:00:00 2001 From: izzy2lost Date: Sun, 12 Apr 2026 20:03:44 -0400 Subject: [PATCH] exit crash fix --- .../java/com/izzy2lost/x1box/MainActivity.kt | 35 +++++++++--- hw/xbox/nv2a/pgraph/vk/renderer.h | 1 + hw/xbox/nv2a/pgraph/vk/surface.c | 57 ++++++++++++++++--- 3 files changed, 79 insertions(+), 14 deletions(-) diff --git a/android/app/src/main/java/com/izzy2lost/x1box/MainActivity.kt b/android/app/src/main/java/com/izzy2lost/x1box/MainActivity.kt index 27a6dfeea2..ef9f0c4f2c 100644 --- a/android/app/src/main/java/com/izzy2lost/x1box/MainActivity.kt +++ b/android/app/src/main/java/com/izzy2lost/x1box/MainActivity.kt @@ -52,6 +52,12 @@ class MainActivity : SDLActivity(), InputManager.InputDeviceListener { private const val TAG = "MainActivity" } + private enum class PendingCloseAction { + NONE, + RETURN_TO_LIBRARY, + QUIT_APP, + } + private data class SnapshotSlotPreview( val slot: Int, val slotLabel: String, @@ -72,6 +78,7 @@ class MainActivity : SDLActivity(), InputManager.InputDeviceListener { private var resumeEmulationOnMenuDismiss = false private var startupSnapshotSlot: Int? = null private var startupSnapshotLoadScheduled = false + private var pendingCloseAction = PendingCloseAction.NONE private lateinit var swipeUpGestureRecognizer: SwipeUpGestureRecognizer private var fpsTextView: TextView? = null private val fpsHandler = Handler(Looper.getMainLooper()) @@ -476,6 +483,9 @@ class MainActivity : SDLActivity(), InputManager.InputDeviceListener { override fun onDestroy() { DebugLog.i(TAG) { "onDestroy()" } + val shouldReturnToLibrary = + pendingCloseAction == PendingCloseAction.RETURN_TO_LIBRARY && !isChangingConfigurations + pendingCloseAction = PendingCloseAction.NONE fpsHandler.removeCallbacks(fpsRunnable) swipeUpGestureRecognizer.reset() resumeEmulationOnMenuDismiss = false @@ -491,6 +501,12 @@ class MainActivity : SDLActivity(), InputManager.InputDeviceListener { inputManager?.unregisterInputDeviceListener(this) window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + if (shouldReturnToLibrary) { + val intent = Intent(this, GameLibraryActivity::class.java).apply { + addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP) + } + startActivity(intent) + } super.onDestroy() } @@ -1049,17 +1065,22 @@ class MainActivity : SDLActivity(), InputManager.InputDeviceListener { } private fun exitToGameLibrary() { - nativeExitEmulation() - val intent = Intent(this, GameLibraryActivity::class.java).apply { - addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP) - } - startActivity(intent) - finish() + requestClose(PendingCloseAction.RETURN_TO_LIBRARY) } private fun quitApp() { + requestClose(PendingCloseAction.QUIT_APP) + } + + private fun requestClose(action: PendingCloseAction) { + if (pendingCloseAction != PendingCloseAction.NONE) { + return + } + pendingCloseAction = action nativeExitEmulation() - finishAffinity() + if (action == PendingCloseAction.QUIT_APP) { + moveTaskToBack(true) + } } override fun getLibraries(): Array = arrayOf( diff --git a/hw/xbox/nv2a/pgraph/vk/renderer.h b/hw/xbox/nv2a/pgraph/vk/renderer.h index 6b9b4204ac..0ff82e210a 100644 --- a/hw/xbox/nv2a/pgraph/vk/renderer.h +++ b/hw/xbox/nv2a/pgraph/vk/renderer.h @@ -1165,6 +1165,7 @@ typedef struct PGRAPHVkState { QTAILQ_HEAD(, SurfaceBinding) invalid_surfaces; QTAILQ_HEAD(, SurfaceBinding) shelved_surfaces; GHashTable *surface_addr_map; + bool surfaces_finalizing; uint32_t surface_list_gen; uint32_t surface_draw_gen; /* Incremented when any surface becomes draw_dirty */ SurfaceBinding *color_binding, *zeta_binding; diff --git a/hw/xbox/nv2a/pgraph/vk/surface.c b/hw/xbox/nv2a/pgraph/vk/surface.c index 132972f3d7..66dded5fba 100644 --- a/hw/xbox/nv2a/pgraph/vk/surface.c +++ b/hw/xbox/nv2a/pgraph/vk/surface.c @@ -36,6 +36,29 @@ static void destroy_surface_image(PGRAPHVkState *r, SurfaceBinding *surface); static void download_surface_deferred(NV2AState *d, SurfaceBinding *surface); /* Forward declaration — defined below, also called from texture.c */ +static bool g_surface_addr_map_missing_logged; + +static GHashTable *surface_addr_map_get(PGRAPHVkState *r, const char *op, + bool allow_recreate) +{ + if (r->surface_addr_map) { + return r->surface_addr_map; + } + + if (!g_surface_addr_map_missing_logged) { + VK_LOG_ERROR("surface_addr_map missing during %s%s", + op, r->surfaces_finalizing ? " (finalizing)" : ""); + g_surface_addr_map_missing_logged = true; + } + + if (!allow_recreate || r->surfaces_finalizing) { + return NULL; + } + + r->surface_addr_map = g_hash_table_new(g_direct_hash, g_direct_equal); + return r->surface_addr_map; +} + void pgraph_vk_set_surface_scale_factor(NV2AState *d, unsigned int scale) { g_config.display.quality.surface_scale = scale < 1 ? 1 : scale; @@ -1493,6 +1516,7 @@ static void unbind_surface(NV2AState *d, bool color) static void invalidate_surface(NV2AState *d, SurfaceBinding *surface) { PGRAPHVkState *r = d->pgraph.vk_renderer_state; + GHashTable *surface_addr_map; trace_nv2a_pgraph_surface_invalidated(surface->vram_addr); @@ -1514,8 +1538,11 @@ static void invalidate_surface(NV2AState *d, SurfaceBinding *surface) unregister_cpu_access_callback(d, surface); - g_hash_table_remove(r->surface_addr_map, - (gpointer)(uintptr_t)surface->vram_addr); + surface_addr_map = surface_addr_map_get(r, "invalidate_surface", false); + if (surface_addr_map) { + g_hash_table_remove(surface_addr_map, + (gpointer)(uintptr_t)surface->vram_addr); + } QTAILQ_REMOVE(&r->surfaces, surface, entry); QTAILQ_INSERT_HEAD(&r->invalid_surfaces, surface, entry); r->surface_list_gen++; @@ -1533,6 +1560,7 @@ static void invalidate_surface(NV2AState *d, SurfaceBinding *surface) static void shelve_surface(NV2AState *d, SurfaceBinding *surface) { PGRAPHVkState *r = d->pgraph.vk_renderer_state; + GHashTable *surface_addr_map; if (surface == r->color_binding) { unbind_surface(d, true); @@ -1543,8 +1571,11 @@ static void shelve_surface(NV2AState *d, SurfaceBinding *surface) unregister_cpu_access_callback(d, surface); - g_hash_table_remove(r->surface_addr_map, - (gpointer)(uintptr_t)surface->vram_addr); + surface_addr_map = surface_addr_map_get(r, "shelve_surface", false); + if (surface_addr_map) { + g_hash_table_remove(surface_addr_map, + (gpointer)(uintptr_t)surface->vram_addr); + } QTAILQ_REMOVE(&r->surfaces, surface, entry); QTAILQ_INSERT_HEAD(&r->shelved_surfaces, surface, entry); r->surface_list_gen++; @@ -1618,14 +1649,18 @@ static void invalidate_overlapping_surfaces(NV2AState *d, static void surface_put(NV2AState *d, SurfaceBinding *surface) { PGRAPHVkState *r = d->pgraph.vk_renderer_state; + GHashTable *surface_addr_map; assert(pgraph_vk_surface_get(d, surface->vram_addr) == NULL); invalidate_overlapping_surfaces(d, surface); register_cpu_access_callback(d, surface); - g_hash_table_insert(r->surface_addr_map, - (gpointer)(uintptr_t)surface->vram_addr, surface); + surface_addr_map = surface_addr_map_get(r, "surface_put", true); + if (surface_addr_map) { + g_hash_table_insert(surface_addr_map, + (gpointer)(uintptr_t)surface->vram_addr, surface); + } QTAILQ_INSERT_HEAD(&r->surfaces, surface, entry); r->surface_list_gen++; } @@ -1633,7 +1668,13 @@ static void surface_put(NV2AState *d, SurfaceBinding *surface) SurfaceBinding *pgraph_vk_surface_get(NV2AState *d, hwaddr addr) { PGRAPHVkState *r = d->pgraph.vk_renderer_state; - return g_hash_table_lookup(r->surface_addr_map, (gpointer)(uintptr_t)addr); + GHashTable *surface_addr_map = surface_addr_map_get(r, "surface_get", false); + + if (!surface_addr_map) { + return NULL; + } + + return g_hash_table_lookup(surface_addr_map, (gpointer)(uintptr_t)addr); } SurfaceBinding *pgraph_vk_surface_get_within(NV2AState *d, hwaddr addr) @@ -3036,6 +3077,7 @@ void pgraph_vk_init_surfaces(PGRAPHState *pg) QTAILQ_INIT(&r->invalid_surfaces); QTAILQ_INIT(&r->shelved_surfaces); r->surface_addr_map = g_hash_table_new(g_direct_hash, g_direct_equal); + r->surfaces_finalizing = false; r->surface_list_gen = 0; r->downloads_pending = false; @@ -3053,6 +3095,7 @@ void pgraph_vk_finalize_surfaces(PGRAPHState *pg) { PGRAPHVkState *r = pg->vk_renderer_state; pgraph_vk_surface_flush(container_of(pg, NV2AState, pgraph)); + r->surfaces_finalizing = true; if (r->surface_addr_map) { g_hash_table_destroy(r->surface_addr_map); r->surface_addr_map = NULL;