Преглед изворни кода

libobs: Add task scheduling features

(This commit also modifies the UI)

Adds the ability to schedule tasks for certain threads
jp9000 пре 5 година
родитељ
комит
8de20ab3be
6 измењених фајлова са 142 додато и 0 уклоњено
  1. 13 0
      UI/obs-app.cpp
  2. 3 0
      UI/window-basic-main.cpp
  3. 10 0
      libobs/obs-internal.h
  4. 23 0
      libobs/obs-video.c
  5. 80 0
      libobs/obs.c
  6. 13 0
      libobs/obs.h

+ 13 - 0
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");

+ 3 - 0
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();
 

+ 10 - 0
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;

+ 23 - 0
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)) {

+ 80 - 0
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;
+}

+ 13 - 0
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 */