GPU: Allow closing window/app to interrupt shader compilation

This commit is contained in:
Connor McLaughlin 2021-02-19 01:48:44 +10:00
parent 413e52b38d
commit 24c2165bb3
8 changed files with 88 additions and 16 deletions

View file

@ -365,13 +365,17 @@ void AndroidHostInterface::EmulationThreadEntryPoint(JNIEnv* env, jobject emulat
if (!m_surface) if (!m_surface)
{ {
Log_ErrorPrint("Emulation thread started without surface set."); Log_ErrorPrint("Emulation thread started without surface set.");
env->CallVoidMethod(m_emulation_activity_object, s_EmulationActivity_method_onEmulationStopped); env->CallVoidMethod(emulation_activity, s_EmulationActivity_method_onEmulationStopped);
return; return;
} }
CreateImGuiContext(); {
std::unique_lock<std::mutex> lock(m_mutex);
m_emulation_thread_running.store(true);
m_emulation_activity_object = emulation_activity; m_emulation_activity_object = emulation_activity;
m_emulation_thread_id = std::this_thread::get_id(); m_emulation_thread_id = std::this_thread::get_id();
}
CreateImGuiContext();
ApplySettings(true); ApplySettings(true);
// Boot system. // Boot system.
@ -398,24 +402,31 @@ void AndroidHostInterface::EmulationThreadEntryPoint(JNIEnv* env, jobject emulat
PowerOffSystem(); PowerOffSystem();
} }
else
// Drain any callbacks so we don't leave things in a screwed-up state for next boot.
{ {
ReportFormattedError("Failed to boot system on emulation thread (file:%s).", boot_params.filename.c_str()); std::unique_lock<std::mutex> lock(m_mutex);
while (!m_callback_queue.empty())
{
auto callback = std::move(m_callback_queue.front());
m_callback_queue.pop_front();
lock.unlock();
callback();
lock.lock();
}
m_emulation_thread_running.store(false);
m_emulation_thread_id = {};
m_emulation_activity_object = {};
m_callbacks_outstanding.store(false);
} }
env->CallVoidMethod(m_emulation_activity_object, s_EmulationActivity_method_onEmulationStopped); env->CallVoidMethod(emulation_activity, s_EmulationActivity_method_onEmulationStopped);
DestroyImGuiContext(); DestroyImGuiContext();
m_emulation_activity_object = {};
} }
void AndroidHostInterface::EmulationThreadLoop(JNIEnv* env) void AndroidHostInterface::EmulationThreadLoop(JNIEnv* env)
{ {
{
std::unique_lock<std::mutex> lock(m_mutex);
m_emulation_thread_running.store(true);
}
env->CallVoidMethod(m_emulation_activity_object, s_EmulationActivity_method_onEmulationStarted); env->CallVoidMethod(m_emulation_activity_object, s_EmulationActivity_method_onEmulationStarted);
for (;;) for (;;)
@ -440,7 +451,6 @@ void AndroidHostInterface::EmulationThreadLoop(JNIEnv* env)
if (m_emulation_thread_stop_request.load()) if (m_emulation_thread_stop_request.load())
{ {
m_emulation_thread_running.store(false);
m_emulation_thread_stop_request.store(false); m_emulation_thread_stop_request.store(false);
return; return;
} }
@ -1059,6 +1069,13 @@ DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_surfaceChanged, jobject obj, j
if (surface && !native_surface) if (surface && !native_surface)
Log_ErrorPrint("ANativeWindow_fromSurface() returned null"); Log_ErrorPrint("ANativeWindow_fromSurface() returned null");
if (!surface && System::GetState() == System::State::Starting)
{
// User switched away from the app while it was compiling shaders.
Log_ErrorPrintf("Surface destroyed while starting, cancelling");
System::CancelPendingStartup();
}
// We should wait for the emu to finish if the surface is being destroyed or changed. // We should wait for the emu to finish if the surface is being destroyed or changed.
AndroidHostInterface* hi = AndroidHelpers::GetNativeClass(env, obj); AndroidHostInterface* hi = AndroidHelpers::GetNativeClass(env, obj);
const bool block = (!native_surface || native_surface != hi->GetSurface()); const bool block = (!native_surface || native_surface != hi->GetSurface());
@ -1376,6 +1393,12 @@ DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_saveState, jobject obj, jboole
DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_saveResumeState, jobject obj, jboolean wait_for_completion) DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_saveResumeState, jobject obj, jboolean wait_for_completion)
{ {
if (!System::IsValid() || System::GetState() == System::State::Starting)
{
// This gets called when the surface is destroyed, which can happen while starting.
return;
}
AndroidHostInterface* hi = AndroidHelpers::GetNativeClass(env, obj); AndroidHostInterface* hi = AndroidHelpers::GetNativeClass(env, obj);
hi->RunOnEmulationThread([hi]() { hi->SaveResumeSaveState(); }, wait_for_completion); hi->RunOnEmulationThread([hi]() { hi->SaveResumeSaveState(); }, wait_for_completion);
} }

View file

@ -509,6 +509,10 @@ bool GPU_HW_D3D11::CompileShaders()
do \ do \
{ \ { \
progress_value++; \ progress_value++; \
if (System::IsStartupCancelled()) \
{ \
return false; \
} \
if (compile_time.GetTimeSeconds() >= 1.0f) \ if (compile_time.GetTimeSeconds() >= 1.0f) \
{ \ { \
compile_time.Reset(); \ compile_time.Reset(); \

View file

@ -519,6 +519,10 @@ bool GPU_HW_OpenGL::CompilePrograms()
do \ do \
{ \ { \
progress_value++; \ progress_value++; \
if (System::IsStartupCancelled()) \
{ \
return false; \
} \
if (compile_time.GetTimeSeconds() >= 1.0f) \ if (compile_time.GetTimeSeconds() >= 1.0f) \
{ \ { \
compile_time.Reset(); \ compile_time.Reset(); \

View file

@ -829,6 +829,10 @@ bool GPU_HW_Vulkan::CompilePipelines()
do \ do \
{ \ { \
progress_value++; \ progress_value++; \
if (System::IsStartupCancelled()) \
{ \
return false; \
} \
if (compile_time.GetTimeSeconds() >= 1.0f) \ if (compile_time.GetTimeSeconds() >= 1.0f) \
{ \ { \
compile_time.Reset(); \ compile_time.Reset(); \

View file

@ -114,9 +114,13 @@ bool HostInterface::BootSystem(const SystemBootParameters& parameters)
CreateAudioStream(); CreateAudioStream();
if (!System::Boot(parameters)) if (!System::Boot(parameters))
{
if (!System::IsStartupCancelled())
{ {
ReportFormattedError( ReportFormattedError(
g_host_interface->TranslateString("System", "System failed to boot. The log may contain more information.")); g_host_interface->TranslateString("System", "System failed to boot. The log may contain more information."));
}
OnSystemDestroyed(); OnSystemDestroyed();
m_audio_stream.reset(); m_audio_stream.reset();
ReleaseHostDisplay(); ReleaseHostDisplay();

View file

@ -85,6 +85,7 @@ static void UpdateRunningGame(const char* path, CDImage* image);
static bool CheckForSBIFile(CDImage* image); static bool CheckForSBIFile(CDImage* image);
static State s_state = State::Shutdown; static State s_state = State::Shutdown;
static std::atomic_bool s_startup_cancelled{false};
static ConsoleRegion s_region = ConsoleRegion::NTSC_U; static ConsoleRegion s_region = ConsoleRegion::NTSC_U;
TickCount g_ticks_per_second = MASTER_CLOCK; TickCount g_ticks_per_second = MASTER_CLOCK;
@ -173,6 +174,17 @@ bool IsValid()
return s_state != State::Shutdown && s_state != State::Starting; return s_state != State::Shutdown && s_state != State::Starting;
} }
bool IsStartupCancelled()
{
return s_startup_cancelled.load();
}
void CancelPendingStartup()
{
if (s_state == State::Starting)
s_startup_cancelled.store(true);
}
ConsoleRegion GetRegion() ConsoleRegion GetRegion()
{ {
return s_region; return s_region;
@ -584,7 +596,10 @@ bool RecreateGPU(GPURenderer renderer, bool update_display /* = true*/)
g_gpu.reset(); g_gpu.reset();
if (!CreateGPU(renderer)) if (!CreateGPU(renderer))
{ {
Panic("Failed to recreate GPU"); if (!IsStartupCancelled())
g_host_interface->ReportError("Failed to recreate GPU.");
System::Shutdown();
return false; return false;
} }
@ -628,6 +643,7 @@ bool Boot(const SystemBootParameters& params)
Assert(s_state == State::Shutdown); Assert(s_state == State::Shutdown);
Assert(s_media_playlist.empty()); Assert(s_media_playlist.empty());
s_state = State::Starting; s_state = State::Starting;
s_startup_cancelled.store(false);
s_region = g_settings.region; s_region = g_settings.region;
if (params.state_stream) if (params.state_stream)
@ -833,6 +849,13 @@ bool Initialize(bool force_software_renderer)
if (!CreateGPU(force_software_renderer ? GPURenderer::Software : g_settings.gpu_renderer)) if (!CreateGPU(force_software_renderer ? GPURenderer::Software : g_settings.gpu_renderer))
return false; return false;
// Was startup cancelled? (e.g. shading compilers took too long and the user closed the application)
if (IsStartupCancelled())
{
Shutdown();
return false;
}
// CPU code cache must happen after GPU, because it might steal our address space. // CPU code cache must happen after GPU, because it might steal our address space.
CPU::CodeCache::Initialize(); CPU::CodeCache::Initialize();

View file

@ -92,6 +92,9 @@ bool IsPaused();
bool IsShutdown(); bool IsShutdown();
bool IsValid(); bool IsValid();
bool IsStartupCancelled();
void CancelPendingStartup();
ConsoleRegion GetRegion(); ConsoleRegion GetRegion();
bool IsPALRegion(); bool IsPALRegion();

View file

@ -825,6 +825,7 @@ void QtHostInterface::powerOffSystem()
{ {
if (!isOnWorkerThread()) if (!isOnWorkerThread())
{ {
System::CancelPendingStartup();
QMetaObject::invokeMethod(this, "powerOffSystem", Qt::QueuedConnection); QMetaObject::invokeMethod(this, "powerOffSystem", Qt::QueuedConnection);
return; return;
} }
@ -839,6 +840,7 @@ void QtHostInterface::powerOffSystemWithoutSaving()
{ {
if (!isOnWorkerThread()) if (!isOnWorkerThread())
{ {
System::CancelPendingStartup();
QMetaObject::invokeMethod(this, "powerOffSystemWithoutSaving", Qt::QueuedConnection); QMetaObject::invokeMethod(this, "powerOffSystemWithoutSaving", Qt::QueuedConnection);
return; return;
} }
@ -849,9 +851,14 @@ void QtHostInterface::powerOffSystemWithoutSaving()
void QtHostInterface::synchronousPowerOffSystem() void QtHostInterface::synchronousPowerOffSystem()
{ {
if (!isOnWorkerThread()) if (!isOnWorkerThread())
{
System::CancelPendingStartup();
QMetaObject::invokeMethod(this, "powerOffSystem", Qt::BlockingQueuedConnection); QMetaObject::invokeMethod(this, "powerOffSystem", Qt::BlockingQueuedConnection);
}
else else
{
powerOffSystem(); powerOffSystem();
}
} }
void QtHostInterface::resetSystem() void QtHostInterface::resetSystem()