diff --git a/dependencies/libobs-d3d11.dll.txt b/dependencies/libobs-d3d11.dll.txt index b8c4cb18bdd5e4..e586e814dd4260 100644 Binary files a/dependencies/libobs-d3d11.dll.txt and b/dependencies/libobs-d3d11.dll.txt differ diff --git a/libobs-d3d11/d3d11-rebuild.cpp b/libobs-d3d11/d3d11-rebuild.cpp index 3496e668be9c78..c3120af7ec690b 100644 --- a/libobs-d3d11/d3d11-rebuild.cpp +++ b/libobs-d3d11/d3d11-rebuild.cpp @@ -400,7 +400,7 @@ const static D3D_FEATURE_LEVEL featureLevels[] = { }; void gs_device::RebuildDevice() -try { +{ ID3D11Device *dev = nullptr; HRESULT hr; @@ -474,6 +474,11 @@ try { adapter.Clear(); factory.Clear(); + + blog(LOG_INFO, ">>> TEST EXCEPTION"); + + throw "test exception"; + /* ----------------------------------------------------------------- */ InitFactory(); @@ -484,7 +489,7 @@ try { createFlags, featureLevels, sizeof(featureLevels) / sizeof(D3D_FEATURE_LEVEL), - D3D11_SDK_VERSION, &device, nullptr, &context); + D3D11_SDK_VERSION, &device, nullptr, &context); if (FAILED(hr)) throw HRError("Failed to create device", hr); @@ -531,7 +536,7 @@ try { break; case gs_type::gs_swap_chain: ((gs_swap_chain *)obj)->Rebuild(dev); - break; + break; case gs_type::gs_timer: ((gs_timer *)obj)->Rebuild(dev); break; @@ -573,12 +578,4 @@ try { for (gs_device_loss &callback : loss_callbacks) callback.device_loss_rebuild(device.Get(), callback.data); - -} catch (const char *error) { - bcrash("Failed to recreate D3D11: %s", error); - -} catch (const HRError &error) { - bcrash("Failed to recreate D3D11: %s (%08lX)", error.str, error.hr); -} catch (...) { - bcrash("Failed to recreate D3D11"); } diff --git a/libobs-d3d11/d3d11-subsystem.cpp b/libobs-d3d11/d3d11-subsystem.cpp index dd3153fd909fa5..b7e1362f8a0642 100644 --- a/libobs-d3d11/d3d11-subsystem.cpp +++ b/libobs-d3d11/d3d11-subsystem.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -947,7 +948,9 @@ gs_device::gs_device(uint32_t adapterIdx) gs_device::~gs_device() { - context->ClearState(); + if (context) { + context->ClearState(); + } } const char *device_get_name(void) @@ -2377,22 +2380,48 @@ bool device_is_present_ready(gs_device_t *device) return ready; } -void device_present(gs_device_t *device) +int device_present(gs_device_t *device, unsigned long long* error_code) { + if (error_code) { + *error_code = 0; + } + gs_swap_chain *const curSwapChain = device->curSwapChain; if (curSwapChain) { device->context->OMSetRenderTargets(0, nullptr, nullptr); device->curFramebufferInvalidate = true; const UINT interval = curSwapChain->hWaitable ? 1 : 0; - const HRESULT hr = curSwapChain->swap->Present(interval, 0); + HRESULT hr = curSwapChain->swap->Present(interval, 0); + + int r = rand() % 10; + if (r == 0) { + hr = DXGI_ERROR_DEVICE_REMOVED; + } + if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) { - device->RebuildDevice(); - } + try { + device->RebuildDevice(); + } catch (const char *error) { + blog(LOG_ERROR, "Failed to recreate D3D11: %s", error); + return OBS_GS_ERR_CAT_DEVICE_REBUILD_ERROR; + } catch (const HRError &error) { + blog(LOG_ERROR, "Failed to recreate D3D11: %s (%08lX)", error.str, error.hr); + if (error_code) { + *error_code = static_cast(error.hr); + } + return OBS_GS_ERR_CAT_DEVICE_REBUILD_ERROR; + } catch (...) { + blog(LOG_ERROR, "Failed to recreate D3D11"); + return OBS_GS_ERR_CAT_DEVICE_REBUILD_ERROR; + } + } } else { blog(LOG_WARNING, "device_present (D3D11): No active swap"); } + + return OBS_GS_ERR_CAT_SUCCESS; } void device_flush(gs_device_t *device) diff --git a/libobs-opengl/gl-windows.c b/libobs-opengl/gl-windows.c index e9f3f09385aa64..6c48f6b8b274e1 100644 --- a/libobs-opengl/gl-windows.c +++ b/libobs-opengl/gl-windows.c @@ -579,7 +579,7 @@ bool device_is_present_ready(gs_device_t *device) return true; } -void device_present(gs_device_t *device) +int device_present(gs_device_t *device, unsigned long* error_code) { if (!SwapBuffers(device->cur_swap->wi->hdc)) { blog(LOG_ERROR, @@ -588,6 +588,8 @@ void device_present(gs_device_t *device) GetLastError()); blog(LOG_ERROR, "device_present (GL) failed"); } + + return 0; } extern void gl_getclientsize(const struct gs_swap_chain *swap, uint32_t *width, diff --git a/libobs/graphics/device-exports.h b/libobs/graphics/device-exports.h index 6200796ed28d70..dcb34197786bc3 100644 --- a/libobs/graphics/device-exports.h +++ b/libobs/graphics/device-exports.h @@ -134,7 +134,7 @@ EXPORT void device_clear(gs_device_t *device, uint32_t clear_flags, const struct vec4 *color, float depth, uint8_t stencil); EXPORT bool device_is_present_ready(gs_device_t *device); -EXPORT void device_present(gs_device_t *device); +EXPORT int device_present(gs_device_t *device, unsigned long long* error_code); EXPORT void device_flush(gs_device_t *device); EXPORT void device_set_cull_mode(gs_device_t *device, enum gs_cull_mode mode); EXPORT enum gs_cull_mode device_get_cull_mode(const gs_device_t *device); diff --git a/libobs/graphics/graphics-internal.h b/libobs/graphics/graphics-internal.h index a32a5c0ad6a9de..34daec37ed1fc6 100644 --- a/libobs/graphics/graphics-internal.h +++ b/libobs/graphics/graphics-internal.h @@ -134,7 +134,7 @@ struct gs_exports { const struct vec4 *color, float depth, uint8_t stencil); bool (*device_is_present_ready)(gs_device_t *device); - void (*device_present)(gs_device_t *device); + int (*device_present)(gs_device_t *device, unsigned long long* error_code); void (*device_flush)(gs_device_t *device); void (*device_set_cull_mode)(gs_device_t *device, enum gs_cull_mode mode); diff --git a/libobs/graphics/graphics.c b/libobs/graphics/graphics.c index f297f4e1168958..a804660d76e084 100644 --- a/libobs/graphics/graphics.c +++ b/libobs/graphics/graphics.c @@ -1953,14 +1953,14 @@ bool gs_is_present_ready(void) return graphics->exports.device_is_present_ready(graphics->device); } -void gs_present(void) +int gs_present(unsigned long long* error_code) { graphics_t *graphics = thread_graphics; if (!gs_valid("gs_present")) - return; + return 0; - graphics->exports.device_present(graphics->device); + return graphics->exports.device_present(graphics->device, error_code); } void gs_flush(void) diff --git a/libobs/graphics/graphics.h b/libobs/graphics/graphics.h index c06984741c4e64..3068747ee99bc3 100644 --- a/libobs/graphics/graphics.h +++ b/libobs/graphics/graphics.h @@ -743,7 +743,7 @@ EXPORT void gs_load_swapchain(gs_swapchain_t *swapchain); EXPORT void gs_clear(uint32_t clear_flags, const struct vec4 *color, float depth, uint8_t stencil); EXPORT bool gs_is_present_ready(void); -EXPORT void gs_present(void); +EXPORT int gs_present(unsigned long long* error_code); EXPORT void gs_flush(void); EXPORT void gs_set_cull_mode(enum gs_cull_mode mode); diff --git a/libobs/obs-defs.h b/libobs/obs-defs.h index 03b1219f828329..888f5bf4733d44 100644 --- a/libobs/obs-defs.h +++ b/libobs/obs-defs.h @@ -50,3 +50,6 @@ #define OBS_VIDEO_INVALID_PARAM -3 #define OBS_VIDEO_CURRENTLY_ACTIVE -4 #define OBS_VIDEO_MODULE_NOT_FOUND -5 + +#define OBS_GS_ERR_CAT_SUCCESS 0 +#define OBS_GS_ERR_CAT_DEVICE_REBUILD_ERROR -1 diff --git a/libobs/obs-display.c b/libobs/obs-display.c index d92d0b357b73e9..6d93c3b3e05662 100644 --- a/libobs/obs-display.c +++ b/libobs/obs-display.c @@ -19,6 +19,9 @@ #include "obs.h" #include "obs-internal.h" +extern gs_error_handler_t gs_error_handler; +extern void* gs_error_handler_param; + bool obs_display_init(struct obs_display *display, const struct gs_init_data *graphics_data) { @@ -236,7 +239,7 @@ static inline void render_display_end() gs_end_scene(); } -void render_display(struct obs_display *display) +bool render_display(struct obs_display *display) { uint32_t cx, cy; bool update_color_space; @@ -276,8 +279,17 @@ void render_display(struct obs_display *display) GS_DEBUG_MARKER_END(); - gs_present(); + blog(LOG_INFO, ">>> render_display() gs_present"); + + unsigned long error_code; + int error_category = gs_present(&error_code); + if (error_category != OBS_GS_ERR_CAT_SUCCESS) { + blog(LOG_INFO, ">>> WILL NOTIFY CALLBACKS"); + gs_error_handler(gs_error_handler_param, error_category, error_code); + return false; + } } + return true; } void obs_display_set_enabled(obs_display_t *display, bool enable) diff --git a/libobs/obs-video.c b/libobs/obs-video.c index 082a6d8e4c34cf..9678dd3b4e5159 100644 --- a/libobs/obs-video.c +++ b/libobs/obs-video.c @@ -77,10 +77,13 @@ static uint64_t tick_sources(uint64_t cur_time, uint64_t last_time) } /* in obs-display.c */ -extern void render_display(struct obs_display *display); +extern bool render_display(struct obs_display *display); -static inline void render_displays(void) +static inline bool render_displays(void) { + blog(LOG_INFO, ">>> render_displays"); + + bool success = true; struct obs_display *display; if (!obs->data.valid) @@ -93,13 +96,20 @@ static inline void render_displays(void) display = obs->data.first_display; while (display) { - render_display(display); + success = render_display(display); + if (!success) { + break; + } display = display->next; } pthread_mutex_unlock(&obs->data.displays_mutex); gs_leave_context(); + + blog(LOG_INFO, ">>> render_displays END"); + + return success; } static inline void set_render_size(uint32_t width, uint32_t height) @@ -1106,8 +1116,10 @@ static inline bool stop_requested(void) bool obs_graphics_thread_loop(struct obs_graphics_context *context) { + blog(LOG_INFO, ">>> obs_graphics_thread_loop"); + /* defer loop break to clean up sources */ - const bool stop_requested = + bool stop_requested = !obs->video.main_mix ? true : video_output_stopped(obs->video.main_mix->video); @@ -1141,7 +1153,10 @@ bool obs_graphics_thread_loop(struct obs_graphics_context *context) profile_end(output_frame_name); profile_start(render_displays_name); - render_displays(); + if (!render_displays()) { + blog(LOG_INFO, ">>> obs_graphics_thread_loop EMERGENCY EXIT"); + stop_requested = true; + } profile_end(render_displays_name); execute_graphics_tasks(); @@ -1171,6 +1186,8 @@ bool obs_graphics_thread_loop(struct obs_graphics_context *context) context->fps_total_frames = 0; } + blog(LOG_INFO, ">>> obs_graphics_thread_loop END"); + return !stop_requested; } diff --git a/libobs/obs.c b/libobs/obs.c index be4d4c7035ed67..b98c50756f7cab 100644 --- a/libobs/obs.c +++ b/libobs/obs.c @@ -33,6 +33,22 @@ extern void add_default_module_paths(void); extern char *find_libobs_data_file(const char *file); static void obs_free_graphics(void); +OBS_NORETURN static void def_gs_error_handler(void *param, int category, unsigned long long code) +{ +#if defined(_WIN32) + blog(LOG_ERROR, "Unhandled graphics error: %d %I64u", category, code); +#else + blog(LOG_ERROR, "Unhandled graphics error: %d %llu", category, code); +#endif + + exit(0); + + UNUSED_PARAMETER(param); +} + +void *gs_error_handler_param = NULL; +gs_error_handler_t gs_error_handler = def_gs_error_handler; + static inline void make_video_info(struct video_output_info *vi, struct obs_video_info *ovi) { @@ -3055,6 +3071,17 @@ void obs_get_audio_monitoring_device(const char **name, const char **id) *id = obs->audio.monitoring_device_id; } +void obs_set_gs_error_handler(gs_error_handler_t handler, void *param) +{ + if (!handler) { + handler = def_gs_error_handler; + } + + gs_error_handler_param = param; + gs_error_handler = handler; +} + + void obs_add_tick_callback(void (*tick)(void *param, float seconds), void *param) { diff --git a/libobs/obs.h b/libobs/obs.h index a0e2acb7f2cbe2..455ad2ff446a2e 100644 --- a/libobs/obs.h +++ b/libobs/obs.h @@ -911,6 +911,9 @@ EXPORT void obs_enum_audio_monitoring_devices(obs_enum_audio_device_cb cb, EXPORT bool obs_set_audio_monitoring_device(const char *name, const char *id); EXPORT void obs_get_audio_monitoring_device(const char **name, const char **id); +typedef void (*gs_error_handler_t)(void *param, int category, unsigned long long code ); +EXPORT void obs_set_gs_error_handler(gs_error_handler_t handler, void* param); + EXPORT void obs_add_tick_callback(void (*tick)(void *param, float seconds), void *param); EXPORT void obs_remove_tick_callback(void (*tick)(void *param, float seconds),