From 5b30d94c1b555a2930d307324b1fad84de907238 Mon Sep 17 00:00:00 2001 From: izzy2lost Date: Sun, 2 Nov 2025 00:27:56 -0400 Subject: [PATCH] Bios now boots without opening games to fix crashes on some devices --- .../src/render/vulkan/SDL_render_vulkan.c | 19 +++++++++ app/src/main/cpp/native-lib.cpp | 10 +++++ app/src/main/cpp/pcsx2/VMManager.cpp | 14 ++++++- app/src/main/cpp/pcsx2/ps2/BiosTools.cpp | 12 ++++++ .../java/com/izzy2lost/psx2/MainActivity.java | 41 +++++++++++-------- .../psx2/SetupWizardDialogFragment.java | 28 ++++--------- 6 files changed, 83 insertions(+), 41 deletions(-) diff --git a/app/src/main/cpp/3rdparty/SDL3/src/render/vulkan/SDL_render_vulkan.c b/app/src/main/cpp/3rdparty/SDL3/src/render/vulkan/SDL_render_vulkan.c index 82ee9f8..ffd293e 100644 --- a/app/src/main/cpp/3rdparty/SDL3/src/render/vulkan/SDL_render_vulkan.c +++ b/app/src/main/cpp/3rdparty/SDL3/src/render/vulkan/SDL_render_vulkan.c @@ -1457,9 +1457,17 @@ static VkResult VULKAN_FindPhysicalDevice(VULKAN_RenderData *rendererData) VkPhysicalDevice physicalDevice = physicalDevices[physicalDeviceIndex]; vkGetPhysicalDeviceProperties(physicalDevice, &rendererData->physicalDeviceProperties); + // Accept Vulkan 1.0 and above (was checking for < 1 which would never be true) + // Some devices report 1.0.x which is valid if (VK_VERSION_MAJOR(rendererData->physicalDeviceProperties.apiVersion) < 1) { + SDL_LogDebug(SDL_LOG_CATEGORY_RENDER, "Skipping device with API version < 1.0"); continue; } + SDL_LogDebug(SDL_LOG_CATEGORY_RENDER, "Found device: %s (API version %d.%d.%d)", + rendererData->physicalDeviceProperties.deviceName, + VK_VERSION_MAJOR(rendererData->physicalDeviceProperties.apiVersion), + VK_VERSION_MINOR(rendererData->physicalDeviceProperties.apiVersion), + VK_VERSION_PATCH(rendererData->physicalDeviceProperties.apiVersion)); vkGetPhysicalDeviceMemoryProperties(physicalDevice, &rendererData->physicalDeviceMemoryProperties); vkGetPhysicalDeviceFeatures(physicalDevice, &rendererData->physicalDeviceFeatures); vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamiliesCount, NULL); @@ -1507,9 +1515,13 @@ static VkResult VULKAN_FindPhysicalDevice(VULKAN_RenderData *rendererData) } if (rendererData->graphicsQueueFamilyIndex == queueFamiliesCount) { // no good queues found + SDL_LogDebug(SDL_LOG_CATEGORY_RENDER, "Device %s: No graphics queue found", + rendererData->physicalDeviceProperties.deviceName); continue; } if (rendererData->presentQueueFamilyIndex == queueFamiliesCount) { // no good queues found + SDL_LogDebug(SDL_LOG_CATEGORY_RENDER, "Device %s: No present queue found", + rendererData->physicalDeviceProperties.deviceName); continue; } result = vkEnumerateDeviceExtensionProperties(physicalDevice, NULL, &deviceExtensionCount, NULL); @@ -1548,8 +1560,12 @@ static VkResult VULKAN_FindPhysicalDevice(VULKAN_RenderData *rendererData) } } if (!hasSwapchainExtension) { + SDL_LogDebug(SDL_LOG_CATEGORY_RENDER, "Device %s: Missing VK_KHR_swapchain extension", + rendererData->physicalDeviceProperties.deviceName); continue; } + SDL_LogDebug(SDL_LOG_CATEGORY_RENDER, "Device %s: Selected as suitable Vulkan device", + rendererData->physicalDeviceProperties.deviceName); rendererData->physicalDevice = physicalDevice; break; } @@ -1557,9 +1573,12 @@ static VkResult VULKAN_FindPhysicalDevice(VULKAN_RenderData *rendererData) SDL_free(queueFamiliesProperties); SDL_free(deviceExtensions); if (!rendererData->physicalDevice) { + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "No viable Vulkan physical devices found. Checked %d device(s).", physicalDeviceCount); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "Devices must support: Vulkan 1.0+, VK_KHR_swapchain, graphics queue, and present queue"); SET_ERROR_MESSAGE("No viable physical devices found"); return VK_ERROR_UNKNOWN; } + SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "Selected Vulkan device: %s", rendererData->physicalDeviceProperties.deviceName); return VK_SUCCESS; } diff --git a/app/src/main/cpp/native-lib.cpp b/app/src/main/cpp/native-lib.cpp index 9b2b0e7..8d2a276 100644 --- a/app/src/main/cpp/native-lib.cpp +++ b/app/src/main/cpp/native-lib.cpp @@ -1172,6 +1172,16 @@ Java_com_izzy2lost_psx2_NativeApp_runVMThread(JNIEnv *env, jclass clazz, // fast_boot : (false:bios->game, true:game) VMBootParameters boot_params; boot_params.filename = _szPath; + + // Enable fast boot when booting BIOS-only (no game loaded) + // This skips the BIOS animation and goes straight to the PS2 menu + Console.WriteLn("runVMThread: path='%s', length=%zu", _szPath.c_str(), _szPath.length()); + if (_szPath.empty()) { + boot_params.fast_boot = true; + Console.WriteLn("BIOS-only boot: Fast boot ENABLED to skip animation"); + } else { + Console.WriteLn("Game boot: path=%s, fast_boot will use default behavior", _szPath.c_str()); + } // Apply per-game settings (if any) before applying core settings ApplyPerGameSettingsForPath(_szPath); diff --git a/app/src/main/cpp/pcsx2/VMManager.cpp b/app/src/main/cpp/pcsx2/VMManager.cpp index 8dd5390..fd2a073 100644 --- a/app/src/main/cpp/pcsx2/VMManager.cpp +++ b/app/src/main/cpp/pcsx2/VMManager.cpp @@ -1406,10 +1406,20 @@ bool VMManager::Initialize(VMBootParameters boot_params) // Read fast boot setting late so it can be overridden per-game. // ELFs must be fast booted, and GS dumps are never fast booted. + // Allow fast boot for BIOS-only boots if explicitly requested via boot_params + const bool has_disc = (CDVDsys_GetSourceType() != CDVD_SourceType::NoDisc); + const bool has_elf = !s_elf_override.empty(); + const bool fast_boot_requested = boot_params.fast_boot.value_or(static_cast(EmuConfig.EnableFastBoot)); + + Console.WriteLn("VMManager::Initialize - Fast boot check: has_disc=%d, has_elf=%d, fast_boot_requested=%d", + has_disc, has_elf, fast_boot_requested); + s_fast_boot_requested = - (boot_params.fast_boot.value_or(static_cast(EmuConfig.EnableFastBoot)) || !s_elf_override.empty()) && - (CDVDsys_GetSourceType() != CDVD_SourceType::NoDisc || !s_elf_override.empty()) && + (fast_boot_requested || has_elf) && + (has_disc || has_elf || fast_boot_requested) && // Allow fast boot for BIOS-only if explicitly requested !GSDumpReplayer::IsReplayingDump(); + + Console.WriteLn("VMManager::Initialize - s_fast_boot_requested=%d", s_fast_boot_requested); if (!s_elf_override.empty()) { diff --git a/app/src/main/cpp/pcsx2/ps2/BiosTools.cpp b/app/src/main/cpp/pcsx2/ps2/BiosTools.cpp index 7e92f11..a98bc21 100644 --- a/app/src/main/cpp/pcsx2/ps2/BiosTools.cpp +++ b/app/src/main/cpp/pcsx2/ps2/BiosTools.cpp @@ -328,6 +328,18 @@ bool LoadBIOS() return false; } + // Check if BIOS is already loaded and matches the current path + // This avoids reloading the same BIOS file on every game launch + if (!BiosRom.empty() && !BiosPath.empty() && BiosPath == path) + { + Console.WriteLn("BIOS already loaded from cache: %s", path.c_str()); + // Just copy the cached BIOS to memory instead of reloading from disk + CopyBIOSToMemory(); + return true; + } + + Console.WriteLn("Loading BIOS from disk: %s", path.c_str()); + auto fp = FileSystem::OpenManagedCFile(path.c_str(), "rb"); if (!fp) return false; diff --git a/app/src/main/java/com/izzy2lost/psx2/MainActivity.java b/app/src/main/java/com/izzy2lost/psx2/MainActivity.java index f7167ea..f2269de 100644 --- a/app/src/main/java/com/izzy2lost/psx2/MainActivity.java +++ b/app/src/main/java/com/izzy2lost/psx2/MainActivity.java @@ -496,20 +496,13 @@ public class MainActivity extends AppCompatActivity implements GamesCoverDialogF f.setCancelable(false); f.show(getSupportFragmentManager(), "setup_wizard"); } else { - // Only auto-open games dialog if this is NOT the first boot after setup - // (Setup wizard handles opening the games dialog on first completion) + // Don't auto-open games dialog on app start + // User can open it manually via home button or controller + // This prevents crashes on devices where BIOS boot + dialog opening is too much at once boolean hasOpenedGamesAfterSetup = prefs.getBoolean("has_opened_games_after_setup", false); - if (hasOpenedGamesAfterSetup) { - try { - final View decor = (getWindow() != null) ? getWindow().getDecorView() : null; - if (decor != null) { - decor.postDelayed(() -> { - if (!isFinishing() && !mSetupWizardActive) { - openGamesDialog(); - } - }, 1600); // small delay to let BIOS boot briefly - } - } catch (Throwable ignored) {} + if (!hasOpenedGamesAfterSetup) { + // Mark as opened so we don't show this message again + prefs.edit().putBoolean("has_opened_games_after_setup", true).apply(); } } @@ -574,6 +567,17 @@ public class MainActivity extends AppCompatActivity implements GamesCoverDialogF if (btn_settings != null) { btn_settings.setOnClickListener(v -> { try { + // Pause the emulation (BIOS or game) before opening drawer + // This prevents crashes on some devices when drawer opens during emulation + if (isThread() && !NativeApp.isPaused()) { + try { + NativeApp.pause(); + android.util.Log.d("MainActivity", "Paused emulation before opening drawer"); + } catch (Throwable e) { + android.util.Log.e("MainActivity", "Error pausing before drawer open: " + e.getMessage()); + } + } + // Open the drawer via post() to avoid reentrancy/layout timing issues // (programmatic open can sometimes race with layout/insets handling) DrawerLayout drawer = findViewById(R.id.drawer_layout); @@ -2501,20 +2505,21 @@ public class MainActivity extends AppCompatActivity implements GamesCoverDialogF mDrawerOpen = false; android.util.Log.d("DrawerTracking", "Drawer closed. Dialog count: " + mOpenDialogCount); if (mOpenDialogCount == 0 && !mDrawerOpen) { - // All dialogs closed and no drawers open, resume the game with a small delay + // All dialogs closed and no drawers open, resume the emulation (game or BIOS) with a small delay View root = findViewById(android.R.id.content); if (root != null) { root.postDelayed(() -> { try { - if (hasSelectedGame() && isThread() && NativeApp.isPaused()) { - android.util.Log.d("DrawerTracking", "Resuming game on drawer close"); + // Resume if emulation thread is running and paused (works for both game and BIOS) + if (isThread() && NativeApp.isPaused()) { + android.util.Log.d("DrawerTracking", "Resuming emulation on drawer close"); NativeApp.resume(); updatePausePlayButton(); } else { - android.util.Log.d("DrawerTracking", "Not resuming - hasGame: " + hasSelectedGame() + ", isThread: " + isThread() + ", isPaused: " + (isThread() ? NativeApp.isPaused() : "N/A")); + android.util.Log.d("DrawerTracking", "Not resuming - isThread: " + isThread() + ", isPaused: " + (isThread() ? NativeApp.isPaused() : "N/A")); } } catch (Throwable e) { - android.util.Log.e("DrawerTracking", "Error resuming game: " + e.getMessage()); + android.util.Log.e("DrawerTracking", "Error resuming emulation: " + e.getMessage()); } }, 100); // Small delay to let drawer fully close } diff --git a/app/src/main/java/com/izzy2lost/psx2/SetupWizardDialogFragment.java b/app/src/main/java/com/izzy2lost/psx2/SetupWizardDialogFragment.java index 5019c91..49bc0be 100644 --- a/app/src/main/java/com/izzy2lost/psx2/SetupWizardDialogFragment.java +++ b/app/src/main/java/com/izzy2lost/psx2/SetupWizardDialogFragment.java @@ -197,20 +197,10 @@ public class SetupWizardDialogFragment extends DialogFragment { dismissAllowingStateLoss(); if (a != null) { // Use adaptive delay based on device capabilities - long manualDelay = getTimeoutForDevice(2000, 4000); // 2s normal, 4s lower-end - android.util.Log.d("SetupWizard", "Using manual delay: " + manualDelay + "ms"); - - final MainActivity act = a; - View decor = act.getWindow() != null ? act.getWindow().getDecorView() : null; - if (decor != null) { - decor.postDelayed(() -> { - act.openGamesDialog(); - }, manualDelay); - } else { - act.runOnUiThread(() -> { - act.openGamesDialog(); - }); - } + // Don't auto-open games dialog after setup wizard + // This prevents crashes when BIOS is still booting + // User can open it manually via home button when ready + android.util.Log.d("SetupWizard", "Setup complete - user can open games dialog via home button"); } } }); @@ -389,13 +379,9 @@ public class SetupWizardDialogFragment extends DialogFragment { MainActivity a = (MainActivity) requireActivity(); a.setSetupWizardActive(false); dismissAllowingStateLoss(); - // Add adaptive delay before opening games dialog - View mainDecor = a.getWindow() != null ? a.getWindow().getDecorView() : null; - if (mainDecor != null) { - mainDecor.postDelayed(() -> { - a.openGamesDialog(); - }, gamesDialogDelay); - } + // Don't auto-open games dialog after setup wizard + // This prevents crashes when BIOS is still booting + // User can open it manually via home button when ready } catch (Throwable ignored) {} }, autoAdvanceDelay); }