浏览代码

Merge pull request #2964 from jpark37/winrt-dispatcher

Set up dispatcher queue for WGC stability
Jim 5 年之前
父节点
当前提交
dedc0c01d5

+ 7 - 3
libobs-winrt/CMakeLists.txt

@@ -7,10 +7,12 @@ add_definitions(-DLIBOBS_EXPORTS)
 include_directories(${CMAKE_CURRENT_BINARY_DIR})
 
 set(libobs-winrt_SOURCES
-	winrt-capture.cpp)
+	winrt-capture.cpp
+	winrt-dispatch.cpp)
 
 set(libobs-winrt_HEADERS
-	winrt-capture.h)
+	winrt-capture.h
+	winrt-dispatch.h)
 
 add_library(libobs-winrt MODULE
 	${libobs-winrt_SOURCES}
@@ -25,10 +27,12 @@ target_precompile_headers(libobs-winrt
 		[["../libobs/util/windows/ComPtr.hpp"]]
 		<obs-module.h>
 		<d3d11.h>
+		<DispatcherQueue.h>
 		<dwmapi.h>
 		<Windows.Graphics.Capture.Interop.h>
 		<winrt/Windows.Foundation.Metadata.h>
-		<winrt/Windows.Graphics.Capture.h>)
+		<winrt/Windows.Graphics.Capture.h>
+		<winrt/Windows.System.h>)
 target_link_libraries(libobs-winrt
 	libobs
 	Dwmapi

+ 43 - 29
libobs-winrt/winrt-capture.cpp

@@ -138,7 +138,6 @@ struct winrt_capture {
 	uint32_t texture_height;
 	D3D11_BOX client_box;
 
-	bool thread_changed;
 	bool active;
 	struct winrt_capture *next;
 
@@ -283,7 +282,7 @@ struct winrt_capture {
 	}
 };
 
-struct winrt_capture *capture_list;
+static struct winrt_capture *capture_list;
 
 static void winrt_capture_device_loss_release(void *data)
 {
@@ -296,12 +295,32 @@ static void winrt_capture_device_loss_release(void *data)
 	capture->frame_pool = nullptr;
 	capture->context = nullptr;
 	capture->device = nullptr;
+	capture->item = nullptr;
 }
 
 static void winrt_capture_device_loss_rebuild(void *device_void, void *data)
 {
 	winrt_capture *capture = static_cast<winrt_capture *>(data);
 
+	auto activation_factory = winrt::get_activation_factory<
+		winrt::Windows::Graphics::Capture::GraphicsCaptureItem>();
+	auto interop_factory =
+		activation_factory.as<IGraphicsCaptureItemInterop>();
+	winrt::Windows::Graphics::Capture::GraphicsCaptureItem item = {nullptr};
+	try {
+		interop_factory->CreateForWindow(
+			capture->window,
+			winrt::guid_of<ABI::Windows::Graphics::Capture::
+					       IGraphicsCaptureItem>(),
+			reinterpret_cast<void **>(winrt::put_abi(item)));
+	} catch (winrt::hresult_error &err) {
+		blog(LOG_ERROR, "CreateForWindow (0x%08X): %ls", err.to_abi(),
+		     err.message().c_str());
+	} catch (...) {
+		blog(LOG_ERROR, "CreateForWindow (0x%08X)",
+		     winrt::to_hresult());
+	}
+
 	ID3D11Device *const d3d_device = (ID3D11Device *)device_void;
 	ComPtr<IDXGIDevice> dxgi_device;
 	if (FAILED(d3d_device->QueryInterface(&dxgi_device)))
@@ -323,7 +342,7 @@ static void winrt_capture_device_loss_rebuild(void *device_void, void *data)
 					DirectXPixelFormat::B8G8R8A8UIntNormalized,
 				2, capture->last_size);
 	const winrt::Windows::Graphics::Capture::GraphicsCaptureSession session =
-		frame_pool.CreateCaptureSession(capture->item);
+		frame_pool.CreateCaptureSession(item);
 
 	/* disable cursor capture if possible since ours performs better */
 #ifdef NTDDI_WIN10_VB
@@ -331,6 +350,7 @@ static void winrt_capture_device_loss_rebuild(void *device_void, void *data)
 		session.IsCursorCaptureEnabled(false);
 #endif
 
+	capture->item = item;
 	capture->device = device;
 	d3d_device->GetImmediateContext(&capture->context);
 	capture->frame_pool = frame_pool;
@@ -342,8 +362,6 @@ static void winrt_capture_device_loss_rebuild(void *device_void, void *data)
 	session.StartCapture();
 }
 
-thread_local bool initialized_tls;
-
 extern "C" EXPORT struct winrt_capture *
 winrt_capture_init(BOOL cursor, HWND window, BOOL client_area)
 try {
@@ -407,9 +425,6 @@ try {
 		session.IsCursorCaptureEnabled(false);
 #endif
 
-	if (capture_list == nullptr)
-		initialized_tls = true;
-
 	struct winrt_capture *capture = new winrt_capture{};
 	capture->window = window;
 	capture->client_area = client_area;
@@ -512,28 +527,8 @@ extern "C" EXPORT void winrt_capture_show_cursor(struct winrt_capture *capture,
 extern "C" EXPORT void winrt_capture_render(struct winrt_capture *capture,
 					    gs_effect_t *effect)
 {
-	if (capture->texture_written) {
-		if (!initialized_tls) {
-			struct winrt_capture *current = capture_list;
-			while (current) {
-				current->thread_changed = true;
-				current = current->next;
-			}
-
-			initialized_tls = true;
-		}
-
-		if (capture->thread_changed) {
-			/* new graphics thread. treat like device loss. */
-			winrt_capture_device_loss_release(capture);
-			winrt_capture_device_loss_rebuild(gs_get_device_obj(),
-							  capture);
-
-			capture->thread_changed = false;
-		}
-
+	if (capture->texture_written)
 		draw_texture(capture, effect);
-	}
 }
 
 extern "C" EXPORT uint32_t
@@ -547,3 +542,22 @@ winrt_capture_height(const struct winrt_capture *capture)
 {
 	return capture ? capture->texture_height : 0;
 }
+
+extern "C" EXPORT void winrt_capture_thread_start()
+{
+	struct winrt_capture *capture = capture_list;
+	void *const device = gs_get_device_obj();
+	while (capture) {
+		winrt_capture_device_loss_rebuild(device, capture);
+		capture = capture->next;
+	}
+}
+
+extern "C" EXPORT void winrt_capture_thread_stop()
+{
+	struct winrt_capture *capture = capture_list;
+	while (capture) {
+		winrt_capture_device_loss_release(capture);
+		capture = capture->next;
+	}
+}

+ 3 - 0
libobs-winrt/winrt-capture.h

@@ -23,6 +23,9 @@ EXPORT void winrt_capture_render(struct winrt_capture *capture,
 EXPORT uint32_t winrt_capture_width(const struct winrt_capture *capture);
 EXPORT uint32_t winrt_capture_height(const struct winrt_capture *capture);
 
+EXPORT void winrt_capture_thread_start();
+EXPORT void winrt_capture_thread_stop();
+
 #ifdef __cplusplus
 }
 #endif

+ 64 - 0
libobs-winrt/winrt-dispatch.cpp

@@ -0,0 +1,64 @@
+extern "C" EXPORT void winrt_initialize()
+{
+	winrt::init_apartment(winrt::apartment_type::single_threaded);
+}
+
+extern "C" EXPORT void winrt_uninitialize()
+{
+	winrt::uninit_apartment();
+}
+
+static winrt::Windows::System::DispatcherQueueController
+CreateDispatcherQueueController()
+{
+	DispatcherQueueOptions options{sizeof(DispatcherQueueOptions),
+				       DQTYPE_THREAD_CURRENT, DQTAT_COM_STA};
+
+	winrt::Windows::System::DispatcherQueueController controller{nullptr};
+	winrt::check_hresult(CreateDispatcherQueueController(
+		options,
+		reinterpret_cast<
+			ABI::Windows::System::IDispatcherQueueController **>(
+			winrt::put_abi(controller))));
+	return controller;
+}
+
+struct winrt_disaptcher {
+	winrt::Windows::System::DispatcherQueueController controller{nullptr};
+};
+
+extern "C" EXPORT struct winrt_disaptcher *winrt_dispatcher_init()
+{
+	struct winrt_disaptcher *dispatcher = NULL;
+	try {
+		if (winrt::Windows::Foundation::Metadata::ApiInformation::
+			    IsApiContractPresent(
+				    L"Windows.Foundation.UniversalApiContract",
+				    5)) {
+			winrt::Windows::System::DispatcherQueueController
+				controller = CreateDispatcherQueueController();
+
+			dispatcher = new struct winrt_disaptcher;
+			dispatcher->controller = std::move(controller);
+		}
+	} catch (const winrt::hresult_error &err) {
+		blog(LOG_ERROR, "winrt_dispatcher_init (0x%08X): %ls",
+		     err.to_abi(), err.message().c_str());
+	} catch (...) {
+		blog(LOG_ERROR, "winrt_dispatcher_init (0x%08X)",
+		     winrt::to_hresult());
+	}
+
+	return dispatcher;
+}
+
+extern "C" EXPORT void
+winrt_dispatcher_free(struct winrt_disaptcher *dispatcher)
+try {
+	delete dispatcher;
+} catch (const winrt::hresult_error &err) {
+	blog(LOG_ERROR, "winrt_dispatcher_free (0x%08X): %ls", err.to_abi(),
+	     err.message().c_str());
+} catch (...) {
+	blog(LOG_ERROR, "winrt_dispatcher_free (0x%08X)", winrt::to_hresult());
+}

+ 19 - 0
libobs-winrt/winrt-dispatch.h

@@ -0,0 +1,19 @@
+#pragma once
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <obs-module.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+EXPORT void winrt_initialize();
+EXPORT void winrt_uninitialize();
+EXPORT struct winrt_disaptcher *winrt_dispatcher_init();
+EXPORT void winrt_dispatcher_free(struct winrt_disaptcher *dispatcher);
+
+#ifdef __cplusplus
+}
+#endif

+ 95 - 3
libobs/obs-video.c

@@ -539,7 +539,7 @@ static const uint8_t *set_gpu_converted_plane(uint32_t width, uint32_t height,
 					      const uint8_t *in, uint8_t *out)
 {
 	if ((width == linesize_input) && (width == linesize_output)) {
-		size_t total = width * height;
+		size_t total = (size_t)width * (size_t)height;
 		memcpy(out, in, total);
 		in += total;
 	} else {
@@ -672,10 +672,12 @@ static inline void copy_rgbx_frame(struct video_frame *output,
 
 	/* if the line sizes match, do a single copy */
 	if (input->linesize[0] == output->linesize[0]) {
-		memcpy(out_ptr, in_ptr, input->linesize[0] * info->height);
+		memcpy(out_ptr, in_ptr,
+		       (size_t)input->linesize[0] * (size_t)info->height);
 	} else {
+		const size_t copy_size = (size_t)info->width * 4;
 		for (size_t y = 0; y < info->height; y++) {
-			memcpy(out_ptr, in_ptr, info->width * 4);
+			memcpy(out_ptr, in_ptr, copy_size);
 			in_ptr += input->linesize[0];
 			out_ptr += output->linesize[0];
 		}
@@ -835,11 +837,97 @@ static void execute_graphics_tasks(void)
 	}
 }
 
+#ifdef _WIN32
+
+struct winrt_exports {
+	void (*winrt_initialize)();
+	void (*winrt_uninitialize)();
+	struct winrt_disaptcher *(*winrt_dispatcher_init)();
+	void (*winrt_dispatcher_free)(struct winrt_disaptcher *dispatcher);
+	void (*winrt_capture_thread_start)();
+	void (*winrt_capture_thread_stop)();
+};
+
+#define WINRT_IMPORT(func)                                        \
+	do {                                                      \
+		exports->func = os_dlsym(module, #func);          \
+		if (!exports->func) {                             \
+			success = false;                          \
+			blog(LOG_ERROR,                           \
+			     "Could not load function '%s' from " \
+			     "module '%s'",                       \
+			     #func, module_name);                 \
+		}                                                 \
+	} while (false)
+
+static bool load_winrt_imports(struct winrt_exports *exports, void *module,
+			       const char *module_name)
+{
+	bool success = true;
+
+	WINRT_IMPORT(winrt_initialize);
+	WINRT_IMPORT(winrt_uninitialize);
+	WINRT_IMPORT(winrt_dispatcher_init);
+	WINRT_IMPORT(winrt_dispatcher_free);
+	WINRT_IMPORT(winrt_capture_thread_start);
+	WINRT_IMPORT(winrt_capture_thread_stop);
+
+	return success;
+}
+
+struct winrt_state {
+	bool loaded;
+	void *winrt_module;
+	struct winrt_exports exports;
+	struct winrt_disaptcher *dispatcher;
+};
+
+static void init_winrt_state(struct winrt_state *winrt)
+{
+	static const char *const module_name = "libobs-winrt";
+
+	winrt->winrt_module = os_dlopen(module_name);
+	winrt->loaded = winrt->winrt_module &&
+			load_winrt_imports(&winrt->exports, winrt->winrt_module,
+					   module_name);
+	winrt->dispatcher = NULL;
+	if (winrt->loaded) {
+		winrt->exports.winrt_initialize();
+		winrt->dispatcher = winrt->exports.winrt_dispatcher_init();
+
+		gs_enter_context(obs->video.graphics);
+		winrt->exports.winrt_capture_thread_start();
+		gs_leave_context();
+	}
+}
+
+static void uninit_winrt_state(struct winrt_state *winrt)
+{
+	if (winrt->winrt_module) {
+		if (winrt->loaded) {
+			winrt->exports.winrt_capture_thread_stop();
+			if (winrt->dispatcher)
+				winrt->exports.winrt_dispatcher_free(
+					winrt->dispatcher);
+			winrt->exports.winrt_uninitialize();
+		}
+
+		os_dlclose(winrt->winrt_module);
+	}
+}
+
+#endif // #ifdef _WIN32
+
 static const char *tick_sources_name = "tick_sources";
 static const char *render_displays_name = "render_displays";
 static const char *output_frame_name = "output_frame";
 void *obs_graphics_thread(void *param)
 {
+#ifdef _WIN32
+	struct winrt_state winrt;
+	init_winrt_state(&winrt);
+#endif // #ifdef _WIN32
+
 	uint64_t last_time = 0;
 	uint64_t interval = video_output_get_frame_time(obs->video.video);
 	uint64_t frame_time_total_ns = 0;
@@ -952,6 +1040,10 @@ void *obs_graphics_thread(void *param)
 			break;
 	}
 
+#ifdef _WIN32
+	uninit_winrt_state(&winrt);
+#endif
+
 	UNUSED_PARAMETER(param);
 	return NULL;
 }