diff --git a/src/r_thread.cpp b/src/r_thread.cpp index 4f10bd8bb..4d0a2100c 100644 --- a/src/r_thread.cpp +++ b/src/r_thread.cpp @@ -127,10 +127,10 @@ void DrawerCommandQueue::Finish() } } }, - [](void *data) + [](void *data, const char *reason, bool fatal) { TryCatchData *d = (TryCatchData*)data; - ReportFatalError(d->queue->active_commands[d->command_index], true); + ReportDrawerError(d->queue->active_commands[d->command_index], true, reason, fatal); }); // Wait for everyone to finish: @@ -139,7 +139,14 @@ void DrawerCommandQueue::Finish() queue->end_condition.wait(end_lock, [&]() { return queue->finished_threads == queue->threads.size(); }); if (!queue->thread_error.IsEmpty()) - I_FatalError("Fatal drawer error: %s", queue->thread_error.GetChars()); + { + static bool first = true; + if (queue->thread_error_fatal) + I_FatalError("%s", queue->thread_error.GetChars()); + else if (first) + Printf("%s\n", queue->thread_error.GetChars()); + first = false; + } // Clean up batch: @@ -212,10 +219,10 @@ void DrawerCommandQueue::StartThreads() } } }, - [](void *data) + [](void *data, const char *reason, bool fatal) { TryCatchData *d = (TryCatchData*)data; - ReportFatalError(d->queue->active_commands[d->command_index], true); + ReportDrawerError(d->queue->active_commands[d->command_index], true, reason, fatal); }); // Notify main thread that we finished: @@ -241,23 +248,31 @@ void DrawerCommandQueue::StopThreads() shutdown_flag = false; } -void DrawerCommandQueue::ReportFatalError(DrawerCommand *command, bool worker_thread) +void DrawerCommandQueue::ReportDrawerError(DrawerCommand *command, bool worker_thread, const char *reason, bool fatal) { if (worker_thread) { std::unique_lock end_lock(Instance()->end_mutex); - if (Instance()->thread_error.IsEmpty()) - Instance()->thread_error = command->DebugInfo(); + if (Instance()->thread_error.IsEmpty() || (!Instance()->thread_error_fatal && fatal)) + { + Instance()->thread_error = reason + (FString)": " + command->DebugInfo(); + Instance()->thread_error_fatal = fatal; + } } else { - I_FatalError("Fatal drawer error: %s", command->DebugInfo().GetChars()); + static bool first = true; + if (fatal) + I_FatalError("%s: %s", reason, command->DebugInfo().GetChars()); + else if (first) + Printf("%s: %s\n", reason, command->DebugInfo().GetChars()); + first = false; } } #ifndef WIN32 -void VectoredTryCatch(void *data, void(*tryBlock)(void *data), void(*catchBlock)(void *data)) +void VectoredTryCatch(void *data, void(*tryBlock)(void *data), void(*catchBlock)(void *data, const char *reason, bool fatal)) { tryBlock(data); } diff --git a/src/r_thread.h b/src/r_thread.h index a2cc8b973..e0aca2a01 100644 --- a/src/r_thread.h +++ b/src/r_thread.h @@ -109,7 +109,7 @@ public: virtual FString DebugInfo() = 0; }; -void VectoredTryCatch(void *data, void(*tryBlock)(void *data), void(*catchBlock)(void *data)); +void VectoredTryCatch(void *data, void(*tryBlock)(void *data), void(*catchBlock)(void *data, const char *reason, bool fatal)); // Manages queueing up commands and executing them on worker threads class DrawerCommandQueue @@ -132,6 +132,7 @@ class DrawerCommandQueue std::condition_variable end_condition; size_t finished_threads = 0; FString thread_error; + bool thread_error_fatal = false; int threaded_render = 0; DrawerThread single_core_thread; @@ -143,7 +144,7 @@ class DrawerCommandQueue void Finish(); static DrawerCommandQueue *Instance(); - static void ReportFatalError(DrawerCommand *command, bool worker_thread); + static void ReportDrawerError(DrawerCommand *command, bool worker_thread, const char *reason, bool fatal); DrawerCommandQueue(); ~DrawerCommandQueue(); @@ -166,10 +167,10 @@ public: T *c = (T*)data; c->Execute(&Instance()->single_core_thread); }, - [](void *data) + [](void *data, const char *reason, bool fatal) { T *c = (T*)data; - ReportFatalError(c, false); + ReportDrawerError(c, false, reason, fatal); }); } else diff --git a/src/win32/i_crash.cpp b/src/win32/i_crash.cpp index 373f902c5..c80d74f8a 100644 --- a/src/win32/i_crash.cpp +++ b/src/win32/i_crash.cpp @@ -3407,23 +3407,97 @@ namespace bool __declspec(thread) DrawerExceptionSetJumpResult; CONTEXT __declspec(thread) DrawerExceptionSetJumpContext; PVOID __declspec(thread) DrawerExceptionHandlerHandle; + char __declspec(thread) *DrawerExceptionReason; + bool __declspec(thread) DrawerExceptionFatal; LONG WINAPI DrawerExceptionHandler(_EXCEPTION_POINTERS *exceptionInfo) { - //RtlRestoreContext(&DrawerExceptionSetJumpContext, exceptionInfo->ExceptionRecord); *exceptionInfo->ContextRecord = DrawerExceptionSetJumpContext; + + DrawerExceptionFatal = false; + switch (exceptionInfo->ExceptionRecord->ExceptionCode) + { + default: DrawerExceptionReason = "Unknown exception code"; break; + case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: DrawerExceptionReason = "Array bounds exceeded"; break; + case EXCEPTION_BREAKPOINT: DrawerExceptionReason = "Breakpoint"; break; + case EXCEPTION_DATATYPE_MISALIGNMENT: DrawerExceptionReason = "Datatype misalignment"; break; + case EXCEPTION_FLT_DENORMAL_OPERAND: DrawerExceptionReason = "Float denormal operand"; break; + case EXCEPTION_FLT_DIVIDE_BY_ZERO: DrawerExceptionReason = "Float divide by zero"; break; + case EXCEPTION_FLT_INEXACT_RESULT: DrawerExceptionReason = "Float inexact result"; break; + case EXCEPTION_FLT_INVALID_OPERATION: DrawerExceptionReason = "Float invalid operation"; break; + case EXCEPTION_FLT_OVERFLOW: DrawerExceptionReason = "Float overflow"; break; + case EXCEPTION_FLT_STACK_CHECK: DrawerExceptionReason = "Float stack check"; break; + case EXCEPTION_FLT_UNDERFLOW: DrawerExceptionReason = "Float underflow"; break; + case EXCEPTION_INT_DIVIDE_BY_ZERO: DrawerExceptionReason = "Int divide by zero"; break; + case EXCEPTION_INT_OVERFLOW: DrawerExceptionReason = "Int overflow"; break; + case EXCEPTION_INVALID_DISPOSITION: DrawerExceptionReason = "Invalid disposition"; break; + case EXCEPTION_NONCONTINUABLE_EXCEPTION: DrawerExceptionReason = "Noncontinuable exception"; break; + case EXCEPTION_PRIV_INSTRUCTION: DrawerExceptionReason = "Priv instruction"; break; + case EXCEPTION_SINGLE_STEP: DrawerExceptionReason = "Single step"; break; + case EXCEPTION_STACK_OVERFLOW: DrawerExceptionReason = "Stack overflow"; break; + + case EXCEPTION_ILLEGAL_INSTRUCTION: + DrawerExceptionReason = "Illegal instruction"; + DrawerExceptionFatal = true; + break; + + case EXCEPTION_ACCESS_VIOLATION: + if (exceptionInfo->ExceptionRecord->ExceptionInformation[0] == 0) + { + DrawerExceptionReason = "Read access violation"; + } + else if (exceptionInfo->ExceptionRecord->ExceptionInformation[0] == 1) + { + DrawerExceptionReason = "Write access violation"; + DrawerExceptionFatal = true; + } + else if (exceptionInfo->ExceptionRecord->ExceptionInformation[0] == 8) + { + DrawerExceptionReason = "User-mode data execution prevention (DEP) violation"; + DrawerExceptionFatal = true; + } + else + { + DrawerExceptionReason = "Unknown access violation"; + DrawerExceptionFatal = true; + } + break; + + case EXCEPTION_IN_PAGE_ERROR: + if (exceptionInfo->ExceptionRecord->ExceptionInformation[0] == 0) + { + DrawerExceptionReason = "In page read error"; + } + else if (exceptionInfo->ExceptionRecord->ExceptionInformation[0] == 1) + { + DrawerExceptionReason = "In page write error"; + DrawerExceptionFatal = true; + } + else if (exceptionInfo->ExceptionRecord->ExceptionInformation[0] == 8) + { + DrawerExceptionReason = "In page user-mode data execution prevention (DEP) error"; + DrawerExceptionFatal = true; + } + else + { + DrawerExceptionReason = "Unknown in page read error"; + DrawerExceptionFatal = true; + } + break; + } + return EXCEPTION_CONTINUE_EXECUTION; } } -void VectoredTryCatch(void *data, void(*tryBlock)(void *data), void(*catchBlock)(void *data)) +void VectoredTryCatch(void *data, void(*tryBlock)(void *data), void(*catchBlock)(void *data, const char *reason, bool fatal)) { DrawerExceptionSetJumpResult = false; RtlCaptureContext(&DrawerExceptionSetJumpContext); if (DrawerExceptionSetJumpResult) { RemoveVectoredExceptionHandler(DrawerExceptionHandlerHandle); - catchBlock(data); + catchBlock(data, DrawerExceptionReason, DrawerExceptionFatal); } else {