瀏覽代碼

libobs-winrt: win-capture: Support client area toggle for WGC

jpark37 5 年之前
父節點
當前提交
2111a3a02f

+ 2 - 0
libobs-winrt/CMakeLists.txt

@@ -24,11 +24,13 @@ target_precompile_headers(libobs-winrt
 		[["../libobs/util/windows/ComPtr.hpp"]]
 		<obs-module.h>
 		<d3d11.h>
+		<dwmapi.h>
 		<Windows.Graphics.Capture.Interop.h>
 		<winrt/Windows.Foundation.Metadata.h>
 		<winrt/Windows.Graphics.Capture.h>)
 target_link_libraries(libobs-winrt
 	libobs
+	Dwmapi
 	windowsapp)
 
 install_obs_core(libobs-winrt)

+ 94 - 16
libobs-winrt/winrt-capture.cpp

@@ -31,8 +31,56 @@ static winrt::com_ptr<T> GetDXGIInterfaceFromObject(
 	return result;
 }
 
+static bool get_client_box(HWND window, uint32_t width, uint32_t height,
+			   D3D11_BOX *client_box)
+{
+	RECT client_rect, window_rect{};
+	POINT upper_left{};
+
+	const bool client_box_available =
+		GetClientRect(window, &client_rect) &&
+		(DwmGetWindowAttribute(window, DWMWA_EXTENDED_FRAME_BOUNDS,
+				       &window_rect,
+				       sizeof(window_rect)) == S_OK) &&
+		ClientToScreen(window, &upper_left);
+	if (client_box_available) {
+		const uint32_t left =
+			(upper_left.x > window_rect.left)
+				? (upper_left.x - window_rect.left)
+				: 0;
+		client_box->left = left;
+
+		const uint32_t top = (upper_left.y > window_rect.top)
+					     ? (upper_left.y - window_rect.top)
+					     : 0;
+		client_box->top = top;
+
+		uint32_t texture_width = 1;
+		if (width > left) {
+			texture_width =
+				min(width - left, (uint32_t)client_rect.right);
+		}
+
+		uint32_t texture_height = 1;
+		if (height > top) {
+			texture_height =
+				min(height - top, (uint32_t)client_rect.bottom);
+		}
+
+		client_box->right = left + texture_width;
+		client_box->bottom = top + texture_height;
+
+		client_box->front = 0;
+		client_box->back = 1;
+	}
+
+	return client_box_available;
+}
+
 struct winrt_capture {
 	bool capture_cursor;
+	HWND window;
+	bool client_area;
 
 	gs_texture_t *texture;
 	bool texture_written;
@@ -48,6 +96,11 @@ struct winrt_capture {
 	winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool::
 		FrameArrived_revoker frame_arrived;
 
+	uint32_t texture_width;
+	uint32_t texture_height;
+	D3D11_BOX client_box;
+	bool client_box_available;
+
 	bool thread_changed;
 	struct winrt_capture *next;
 
@@ -70,23 +123,45 @@ struct winrt_capture {
 		D3D11_TEXTURE2D_DESC desc;
 		frame_surface->GetDesc(&desc);
 
+		client_box_available = false;
+		if (client_area) {
+			client_box_available = get_client_box(
+				window, desc.Width, desc.Height, &client_box);
+		}
+
+		if (client_box_available) {
+			texture_width = client_box.right - client_box.left;
+			texture_height = client_box.bottom - client_box.top;
+		} else {
+			texture_width = desc.Width;
+			texture_height = desc.Height;
+		}
+
 		if (texture) {
-			if (desc.Width != gs_texture_get_width(texture) ||
-			    desc.Height != gs_texture_get_height(texture)) {
+			if (texture_width != gs_texture_get_width(texture) ||
+			    texture_height != gs_texture_get_height(texture)) {
 				gs_texture_destroy(texture);
 				texture = nullptr;
 			}
 		}
 
 		if (!texture) {
-			texture = gs_texture_create(desc.Width, desc.Height,
-						    GS_BGRA, 1, nullptr, 0);
+			texture = gs_texture_create(texture_width,
+						    texture_height, GS_BGRA, 1,
+						    nullptr, 0);
 		}
 
-		/* if they gave an SRV, we could avoid this copy */
-		context->CopyResource(
-			(ID3D11Texture2D *)gs_texture_get_obj(texture),
-			frame_surface.get());
+		if (client_box_available) {
+			context->CopySubresourceRegion(
+				(ID3D11Texture2D *)gs_texture_get_obj(texture),
+				0, 0, 0, 0, frame_surface.get(), 0,
+				&client_box);
+		} else {
+			/* if they gave an SRV, we could avoid this copy */
+			context->CopyResource(
+				(ID3D11Texture2D *)gs_texture_get_obj(texture),
+				frame_surface.get());
+		}
 
 		texture_written = true;
 
@@ -160,8 +235,8 @@ static void winrt_capture_device_loss_rebuild(void *device_void, void *data)
 
 thread_local bool initialized_tls;
 
-extern "C" EXPORT struct winrt_capture *winrt_capture_init(bool cursor,
-							   HWND window)
+extern "C" EXPORT struct winrt_capture *
+winrt_capture_init(bool cursor, HWND window, bool client_area)
 {
 	ID3D11Device *const d3d_device = (ID3D11Device *)gs_get_device_obj();
 	ComPtr<IDXGIDevice> dxgi_device;
@@ -192,8 +267,9 @@ extern "C" EXPORT struct winrt_capture *winrt_capture_init(bool cursor,
 					       IGraphicsCaptureItem>(),
 			reinterpret_cast<void **>(winrt::put_abi(item)));
 	} catch (winrt::hresult_invalid_argument &) {
-		blog(LOG_WARNING, "[winrt_capture_init] Failed to "
-				  "create GraphicsCaptureItem");
+		/* too spammy */
+		//blog(LOG_WARNING, "[winrt_capture_init] Failed to "
+		//		  "create GraphicsCaptureItem");
 		return nullptr;
 	}
 
@@ -216,6 +292,8 @@ extern "C" EXPORT struct winrt_capture *winrt_capture_init(bool cursor,
 
 	struct winrt_capture *capture = new winrt_capture{};
 	capture->capture_cursor = cursor;
+	capture->window = window;
+	capture->client_area = client_area;
 	capture->item = item;
 	capture->device = device;
 	d3d_device->GetImmediateContext(&capture->context);
@@ -315,14 +393,14 @@ extern "C" EXPORT void winrt_capture_render(struct winrt_capture *capture,
 	}
 }
 
-extern "C" EXPORT int32_t
+extern "C" EXPORT uint32_t
 winrt_capture_width(const struct winrt_capture *capture)
 {
-	return capture ? capture->last_size.Width : 0;
+	return capture ? capture->texture_width : 0;
 }
 
-extern "C" EXPORT int32_t
+extern "C" EXPORT uint32_t
 winrt_capture_height(const struct winrt_capture *capture)
 {
-	return capture ? capture->last_size.Height : 0;
+	return capture ? capture->texture_height : 0;
 }

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

@@ -10,13 +10,14 @@ extern "C" {
 #endif
 
 EXPORT bool winrt_capture_supported();
-EXPORT struct winrt_capture *winrt_capture_init(bool cursor, HWND window);
+EXPORT struct winrt_capture *winrt_capture_init(bool cursor, HWND window,
+						bool client_area);
 EXPORT void winrt_capture_free(struct winrt_capture *capture);
 
 EXPORT void winrt_capture_render(struct winrt_capture *capture,
 				 gs_effect_t *effect);
-EXPORT int32_t winrt_capture_width(const struct winrt_capture *capture);
-EXPORT int32_t winrt_capture_height(const 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);
 
 #ifdef __cplusplus
 }

+ 1 - 0
plugins/win-capture/data/locale/en-US.ini

@@ -11,6 +11,7 @@ WindowCapture.Priority.Class="Match title, otherwise find window of same type"
 WindowCapture.Priority.Exe="Match title, otherwise find window of same executable"
 CaptureCursor="Capture Cursor"
 Compatibility="Multi-adapter Compatibility"
+ClientArea="Client Area"
 SLIFix="SLI/Crossfire Capture Mode (Slow)"
 AllowTransparency="Allow Transparency"
 Monitor="Display"

+ 15 - 4
plugins/win-capture/window-capture.c

@@ -19,6 +19,7 @@
 #define TEXT_MATCH_EXE      obs_module_text("WindowCapture.Priority.Exe")
 #define TEXT_CAPTURE_CURSOR obs_module_text("CaptureCursor")
 #define TEXT_COMPATIBILITY  obs_module_text("Compatibility")
+#define TEXT_CLIENT_AREA    obs_module_text("ClientArea")
 
 /* clang-format on */
 
@@ -26,12 +27,13 @@
 
 struct winrt_exports {
 	bool *(*winrt_capture_supported)();
-	struct winrt_capture *(*winrt_capture_init)(bool cursor, HWND window);
+	struct winrt_capture *(*winrt_capture_init)(bool cursor, HWND window,
+						    bool client_area);
 	void (*winrt_capture_free)(struct winrt_capture *capture);
 	void (*winrt_capture_render)(struct winrt_capture *capture,
 				     gs_effect_t *effect);
-	int32_t (*winrt_capture_width)(const struct winrt_capture *capture);
-	int32_t (*winrt_capture_height)(const struct winrt_capture *capture);
+	uint32_t (*winrt_capture_width)(const struct winrt_capture *capture);
+	uint32_t (*winrt_capture_height)(const struct winrt_capture *capture);
 };
 
 enum window_capture_method {
@@ -50,6 +52,7 @@ struct window_capture {
 	enum window_priority priority;
 	bool cursor;
 	bool compatibility;
+	bool client_area;
 	bool use_wildcards; /* TODO */
 
 	struct dc_capture capture;
@@ -139,6 +142,7 @@ static void update_settings(struct window_capture *wc, obs_data_t *s)
 	wc->cursor = obs_data_get_bool(s, "cursor");
 	wc->use_wildcards = obs_data_get_bool(s, "use_wildcards");
 	wc->compatibility = obs_data_get_bool(s, "compatibility");
+	wc->client_area = obs_data_get_bool(s, "client_area");
 }
 
 /* ------------------------------------------------------------------------- */
@@ -252,18 +256,23 @@ static void wc_defaults(obs_data_t *defaults)
 	obs_data_set_default_int(defaults, "method", METHOD_AUTO);
 	obs_data_set_default_bool(defaults, "cursor", true);
 	obs_data_set_default_bool(defaults, "compatibility", false);
+	obs_data_set_default_bool(defaults, "client_area", true);
 }
 
 static void update_settings_visibility(obs_properties_t *props,
 				       enum window_capture_method method)
 {
 	const bool bitblt_options = method == METHOD_BITBLT;
+	const bool wgc_options = method == METHOD_WGC;
 
 	obs_property_t *p = obs_properties_get(props, "cursor");
 	obs_property_set_visible(p, bitblt_options);
 
 	p = obs_properties_get(props, "compatibility");
 	obs_property_set_visible(p, bitblt_options);
+
+	p = obs_properties_get(props, "client_area");
+	obs_property_set_visible(p, wgc_options);
 }
 
 static bool wc_capture_method_changed(obs_properties_t *props,
@@ -321,6 +330,8 @@ static obs_properties_t *wc_properties(void *data)
 
 	obs_properties_add_bool(ppts, "compatibility", TEXT_COMPATIBILITY);
 
+	obs_properties_add_bool(ppts, "client_area", TEXT_CLIENT_AREA);
+
 	return ppts;
 }
 
@@ -441,7 +452,7 @@ static void wc_tick(void *data, float seconds)
 	} else if (wc->method == METHOD_WGC) {
 		if (wc->window && (wc->capture_winrt == NULL)) {
 			wc->capture_winrt = wc->exports.winrt_capture_init(
-				wc->cursor, wc->window);
+				wc->cursor, wc->window, wc->client_area);
 		}
 	}