diff --git a/aurora-main/include/aurora/aurora.h b/aurora-main/include/aurora/aurora.h index ff9120f..237bfbf 100644 --- a/aurora-main/include/aurora/aurora.h +++ b/aurora-main/include/aurora/aurora.h @@ -295,12 +295,20 @@ typedef struct { bool xrHeadsetOnly; } AuroraConfig; +typedef enum { + AURORA_INITIALIZATION_SUCCESS = 0, + AURORA_INITIALIZATION_GRAPHICS_UNAVAILABLE = 1, +} AuroraInitializationStatus; + typedef struct { AuroraBackend backend; const char* userPath; const char* cachePath; SDL_Window* window; AuroraWindowSize windowSize; + AuroraInitializationStatus initializationStatus; + // On failure, owned by SDL on the calling thread. Copy before another SDL call. + const char* initializationError; } AuroraInfo; AuroraInfo aurora_initialize(int argc, char* argv[], const AuroraConfig* config); diff --git a/aurora-main/lib/aurora.cpp b/aurora-main/lib/aurora.cpp index 5a12c9f..52c5a34 100644 --- a/aurora-main/lib/aurora.cpp +++ b/aurora-main/lib/aurora.cpp @@ -1229,15 +1229,23 @@ AuroraInfo initialize(int argc, char* argv[], const AuroraConfig& config) noexce const AuroraBackend requestedBackend = config.desiredBackend; AuroraBackend selectedBackend = requestedBackend; bool windowCreated = false; + std::string firstGraphicsError; + const auto rememberGraphicsError = [&] { + if (firstGraphicsError.empty() && SDL_GetError()[0] != '\0') { + firstGraphicsError = SDL_GetError(); + } + }; if (selectedBackend != BACKEND_AUTO) { Log.info("Requested graphics backend: {}", backend_name(selectedBackend)); if (window::create_window(selectedBackend)) { if (webgpu::initialize(selectedBackend)) { windowCreated = true; } else { + rememberGraphicsError(); window::destroy_window(); } } else { + rememberGraphicsError(); Log.error("Failed to create a window for backend {}: {}", backend_name(selectedBackend), SDL_GetError()); } if (!windowCreated) { @@ -1254,18 +1262,28 @@ AuroraInfo initialize(int argc, char* argv[], const AuroraConfig& config) noexce for (const auto backendType : PreferredBackendOrder) { selectedBackend = backendType; if (!window::create_window(selectedBackend)) { + rememberGraphicsError(); continue; } if (webgpu::initialize(selectedBackend)) { windowCreated = true; break; } else { + rememberGraphicsError(); window::destroy_window(); } } } - ASSERT(windowCreated, "Error creating window: {}", SDL_GetError()); + if (!windowCreated) { + if (firstGraphicsError.empty()) firstGraphicsError = "No supported graphics backend is available"; + SDL_SetError("%s", firstGraphicsError.c_str()); + Log.error("Graphics initialization failed: {}", firstGraphicsError); + return { + .initializationStatus = AURORA_INITIALIZATION_GRAPHICS_UNAVAILABLE, + .initializationError = SDL_GetError(), + }; + } if (requestedBackend != BACKEND_AUTO && selectedBackend != requestedBackend) { Log.error( "Graphics backend fallback in effect: video.graphics_api requested {}, " diff --git a/aurora-main/lib/imgui.cpp b/aurora-main/lib/imgui.cpp index f3854d4..0cdd07b 100644 --- a/aurora-main/lib/imgui.cpp +++ b/aurora-main/lib/imgui.cpp @@ -85,18 +85,30 @@ void initialize() noexcept { void shutdown() noexcept { ZoneScoped; - if (g_useSdlRenderer) { - ImGui_ImplSDLRenderer3_Shutdown(); - } else { - ImGui_ImplWGPU_Shutdown(); + // Startup can fail before either backend initializes. A context alone does + // not mean its renderer/platform backend owns resources to release. + if (ImGui::GetCurrentContext() != nullptr) { + ImGuiIO& io = ImGui::GetIO(); + if (io.BackendRendererUserData != nullptr) { + if (g_useSdlRenderer) { + ImGui_ImplSDLRenderer3_Shutdown(); + } else { + ImGui_ImplWGPU_Shutdown(); + } + } + if (io.BackendPlatformUserData != nullptr) { + ImGui_ImplSDL3_Shutdown(); + } + ImGui::DestroyContext(); } - ImGui_ImplSDL3_Shutdown(); - ImGui::DestroyContext(); for (const auto& texture : g_sdlTextures) { SDL_DestroyTexture(texture); } g_sdlTextures.clear(); g_wgpuTextures.clear(); + g_useSdlRenderer = false; + g_scale = 0.f; + g_frameDataBuilt = true; } void process_event(const SDL_Event& event) noexcept { diff --git a/aurora-main/lib/webgpu/gpu.cpp b/aurora-main/lib/webgpu/gpu.cpp index 0876bb6..6b015aa 100644 --- a/aurora-main/lib/webgpu/gpu.cpp +++ b/aurora-main/lib/webgpu/gpu.cpp @@ -627,12 +627,16 @@ bool initialize(AuroraBackend auroraBackend) { g_adapter = std::move(adapter); } else { Log.warn("Adapter request failed: {}", message); + const std::string_view reason{message}; + SDL_SetError("Graphics adapter unavailable: %.*s", + static_cast(std::min(reason.size(), 512)), reason.data()); } }); const auto status = g_instance.WaitAny(future, 5000000000); if (status != wgpu::WaitStatus::Success) { Log.error("Failed to create {} adapter: {}", magic_enum::enum_name(backend), magic_enum::enum_name(status)); + SDL_SetError("Graphics adapter request did not complete within its startup deadline"); return false; } if (!g_adapter) { @@ -845,11 +849,16 @@ bool initialize(AuroraBackend auroraBackend) { g_device = std::move(device); } else { Log.warn("Device request failed: {}", message); + const std::string_view reason{message}; + SDL_SetError("Graphics device unavailable: %.*s", + static_cast(std::min(reason.size(), 512)), + reason.data()); } }); const auto status = g_instance.WaitAny(future, 5000000000); if (status != wgpu::WaitStatus::Success) { Log.error("Failed to create device: {}", magic_enum::enum_name(status)); + SDL_SetError("Graphics device request did not complete within its startup deadline"); return false; } if (!g_device) { diff --git a/runtime/src/main.cpp b/runtime/src/main.cpp index abc2e9b..539cd4a 100644 --- a/runtime/src/main.cpp +++ b/runtime/src/main.cpp @@ -1551,6 +1551,10 @@ int RuntimeMain(int argc, char** argv) { WiiRemoteInput::ConfigureSdlHints(RuntimeConfigFile::WiiRemotesEnabled(true)); const AuroraInfo auroraInfo = aurora_initialize(0, nullptr, &auroraConfig); + if (auroraInfo.initializationStatus != AURORA_INITIALIZATION_SUCCESS) { + throw std::runtime_error(auroraInfo.initializationError != nullptr + ? auroraInfo.initializationError : "No supported graphics backend is available"); + } if (requestedBackend != BACKEND_AUTO && auroraInfo.backend != requestedBackend) { RT_LOG(RT_TAG_RUNTIME) << "graphics_api=\"" << backend << "\" is not available on this system; aurora fell back to \""