diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp index bcee173b3..b6f0b7d59 100644 --- a/UI/obs-app.cpp +++ b/UI/obs-app.cpp @@ -1327,6 +1327,17 @@ void OBSApp::Exec(VoidFunc func) func(); } +static void ui_task_handler(obs_task_t task, void *param, bool wait) +{ + auto doTask = [=]() { + /* to get clang-format to behave */ + task(param); + }; + QMetaObject::invokeMethod(App(), "Exec", + wait ? WaitConnection() : Qt::AutoConnection, + Q_ARG(VoidFunc, doTask)); +} + bool OBSApp::OBSInit() { ProfileScope("OBSApp::OBSInit"); @@ -1338,6 +1349,8 @@ bool OBSApp::OBSInit() if (!StartupOBS(locale.c_str(), GetProfilerNameStore())) return false; + obs_set_ui_task_handler(ui_task_handler); + #ifdef _WIN32 bool browserHWAccel = config_get_bool(globalConfig, "General", "BrowserHWAccel"); diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 3bb07b57c..48f2d422a 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -2267,6 +2267,9 @@ void OBSBasic::ClearHotkeys() OBSBasic::~OBSBasic() { + /* clear out UI event queue */ + QApplication::sendPostedEvents(App()); + if (updateCheckThread && updateCheckThread->isRunning()) updateCheckThread->wait(); diff --git a/libobs/obs-internal.h b/libobs/obs-internal.h index e2d1af749..b67446cac 100644 --- a/libobs/obs-internal.h +++ b/libobs/obs-internal.h @@ -234,6 +234,11 @@ struct obs_tex_frame { bool released; }; +struct obs_task_info { + obs_task_t task; + void *param; +}; + struct obs_core_video { graphics_t *graphics; gs_stagesurf_t *copy_surfaces[NUM_TEXTURES][NUM_CHANNELS]; @@ -306,6 +311,9 @@ struct obs_core_video { gs_effect_t *deinterlace_yadif_2x_effect; struct obs_video_info ovi; + + pthread_mutex_t task_mutex; + struct circlebuf tasks; }; struct audio_monitor; @@ -420,6 +428,8 @@ struct obs_core { struct obs_core_audio audio; struct obs_core_data data; struct obs_core_hotkeys hotkeys; + + obs_task_handler_t ui_task_handler; }; extern struct obs_core *obs; diff --git a/libobs/obs-video.c b/libobs/obs-video.c index 2e610af0d..894a9fcf6 100644 --- a/libobs/obs-video.c +++ b/libobs/obs-video.c @@ -816,6 +816,25 @@ static void clear_gpu_frame_data(void) } #endif +extern THREAD_LOCAL bool is_graphics_thread; + +static void execute_graphics_tasks(void) +{ + struct obs_core_video *video = &obs->video; + bool tasks_remaining = true; + + while (tasks_remaining) { + pthread_mutex_lock(&video->task_mutex); + if (video->tasks.size) { + struct obs_task_info info; + circlebuf_pop_front(&video->tasks, &info, sizeof(info)); + info.task(info.param); + } + tasks_remaining = !!video->tasks.size; + pthread_mutex_unlock(&video->task_mutex); + } +} + static const char *tick_sources_name = "tick_sources"; static const char *render_displays_name = "render_displays"; static const char *output_frame_name = "output_frame"; @@ -832,6 +851,8 @@ void *obs_graphics_thread(void *param) bool raw_was_active = false; bool was_active = false; + is_graphics_thread = true; + obs->video.video_time = os_gettime_ns(); obs->video.video_frame_interval_ns = interval; @@ -883,6 +904,8 @@ void *obs_graphics_thread(void *param) last_time = tick_sources(obs->video.video_time, last_time); profile_end(tick_sources_name); + execute_graphics_tasks(); + #ifdef _WIN32 MSG msg; while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { diff --git a/libobs/obs.c b/libobs/obs.c index c3c8bfc03..da806d00a 100644 --- a/libobs/obs.c +++ b/libobs/obs.c @@ -426,6 +426,8 @@ static int obs_init_video(struct obs_video_info *ovi) return OBS_VIDEO_FAIL; if (pthread_mutex_init(&video->gpu_encoder_mutex, NULL) < 0) return OBS_VIDEO_FAIL; + if (pthread_mutex_init(&video->task_mutex, NULL) < 0) + return OBS_VIDEO_FAIL; errorcode = pthread_create(&video->video_thread, NULL, obs_graphics_thread, obs); @@ -521,6 +523,10 @@ static void obs_free_video(void) pthread_mutex_init_value(&video->gpu_encoder_mutex); da_free(video->gpu_encoders); + pthread_mutex_destroy(&video->task_mutex); + pthread_mutex_init_value(&video->task_mutex); + circlebuf_free(&video->tasks); + video->gpu_encoder_active = 0; video->cur_texture = 0; } @@ -843,6 +849,7 @@ static bool obs_init(const char *locale, const char *module_config_path, pthread_mutex_init_value(&obs->audio.monitoring_mutex); pthread_mutex_init_value(&obs->video.gpu_encoder_mutex); + pthread_mutex_init_value(&obs->video.task_mutex); obs->name_store_owned = !store; obs->name_store = store ? store : profiler_name_store_create(); @@ -2577,3 +2584,76 @@ bool obs_nv12_tex_active(void) return video->using_nv12_tex; } + +/* ------------------------------------------------------------------------- */ +/* task stuff */ + +struct task_wait_info { + obs_task_t task; + void *param; + os_event_t *event; +}; + +static void task_wait_callback(void *param) +{ + struct task_wait_info *info = param; + info->task(info->param); + os_event_signal(info->event); +} + +THREAD_LOCAL bool is_graphics_thread = false; + +static bool in_task_thread(enum obs_task_type type) +{ + /* NOTE: OBS_TASK_UI is handled independently */ + + if (type == OBS_TASK_GRAPHICS) + return is_graphics_thread; + + assert(false); + return false; +} + +void obs_queue_task(enum obs_task_type type, obs_task_t task, void *param, + bool wait) +{ + if (!obs) + return; + + if (type == OBS_TASK_UI) { + if (obs->ui_task_handler) { + obs->ui_task_handler(task, param, wait); + } else { + blog(LOG_ERROR, "UI task could not be queued, " + "there's no UI task handler!"); + } + } else { + if (in_task_thread(type)) { + task(param); + } else if (wait) { + struct task_wait_info info = { + .task = task, + .param = param, + }; + + os_event_init(&info.event, OS_EVENT_TYPE_MANUAL); + obs_queue_task(type, task_wait_callback, &info, false); + os_event_wait(info.event); + os_event_destroy(info.event); + } else { + struct obs_core_video *video = &obs->video; + struct obs_task_info info = {task, param}; + + pthread_mutex_lock(&video->task_mutex); + circlebuf_push_back(&video->tasks, &info, sizeof(info)); + pthread_mutex_unlock(&video->task_mutex); + } + } +} + +void obs_set_ui_task_handler(obs_task_handler_t handler) +{ + if (!obs) + return; + obs->ui_task_handler = handler; +} diff --git a/libobs/obs.h b/libobs/obs.h index dcb175286..0d3851ceb 100644 --- a/libobs/obs.h +++ b/libobs/obs.h @@ -745,6 +745,19 @@ EXPORT void obs_apply_private_data(obs_data_t *settings); EXPORT void obs_set_private_data(obs_data_t *settings); EXPORT obs_data_t *obs_get_private_data(void); +typedef void (*obs_task_t)(void *param); + +enum obs_task_type { + OBS_TASK_UI, + OBS_TASK_GRAPHICS, +}; + +EXPORT void obs_queue_task(enum obs_task_type type, obs_task_t task, + void *param, bool wait); + +typedef void (*obs_task_handler_t)(obs_task_t task, void *param, bool wait); +EXPORT void obs_set_ui_task_handler(obs_task_handler_t handler); + /* ------------------------------------------------------------------------- */ /* View context */