瀏覽代碼

win-capture: Add DXGI/WGC HDR support

HDR for DXGI/WGC display capture, and WGC window capture.

Also add fast SDR paths for BitBlt to skip color conversions.
jpark37 3 年之前
父節點
當前提交
93fe82d1ce
共有 3 個文件被更改,包括 170 次插入47 次删除
  1. 70 42
      plugins/win-capture/dc-capture.c
  2. 71 5
      plugins/win-capture/duplicator-monitor-capture.c
  3. 29 0
      plugins/win-capture/window-capture.c

+ 70 - 42
plugins/win-capture/dc-capture.c

@@ -69,11 +69,20 @@ void dc_capture_init(struct dc_capture *capture, int x, int y, uint32_t width,
 		bih->biHeight = height;
 		bih->biPlanes = 1;
 
-		capture->hdc = CreateCompatibleDC(NULL);
-		capture->bmp =
-			CreateDIBSection(capture->hdc, &bi, DIB_RGB_COLORS,
-					 (void **)&capture->bits, NULL, 0);
-		capture->old_bmp = SelectObject(capture->hdc, capture->bmp);
+		const HDC hdc = CreateCompatibleDC(NULL);
+		if (hdc) {
+			const HBITMAP bmp = CreateDIBSection(
+				capture->hdc, &bi, DIB_RGB_COLORS,
+				(void **)&capture->bits, NULL, 0);
+			if (bmp) {
+				capture->hdc = hdc;
+				capture->bmp = bmp;
+				capture->old_bmp = SelectObject(capture->hdc,
+								capture->bmp);
+			} else {
+				DeleteDC(capture->hdc);
+			}
+		}
 	}
 }
 
@@ -179,48 +188,67 @@ void dc_capture_capture(struct dc_capture *capture, HWND window)
 	capture->texture_written = true;
 }
 
-static void draw_texture(struct dc_capture *capture, bool texcoords_centered)
+void dc_capture_render(struct dc_capture *capture, bool texcoords_centered)
 {
-	gs_effect_t *effect = obs_get_base_effect(OBS_EFFECT_OPAQUE);
-	gs_technique_t *tech = gs_effect_get_technique(effect, "Draw");
-	gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image");
-
-	gs_texture_t *texture = capture->texture;
-	const bool compatibility = capture->compatibility;
-	bool linear_sample = compatibility;
-	if (!linear_sample && !texcoords_centered) {
-		gs_texture_t *const extra_texture = capture->extra_texture;
-		gs_copy_texture(extra_texture, texture);
-		texture = extra_texture;
-		linear_sample = true;
-	}
+	if (capture->valid && capture->texture_written) {
+		gs_texture_t *texture = capture->texture;
+		const bool compatibility = capture->compatibility;
+		bool linear_sample = compatibility;
+		if (!linear_sample && !texcoords_centered) {
+			gs_texture_t *const extra_texture =
+				capture->extra_texture;
+			gs_copy_texture(extra_texture, texture);
+			texture = extra_texture;
+			linear_sample = true;
+		}
 
-	const bool previous = gs_framebuffer_srgb_enabled();
-	gs_enable_framebuffer_srgb(linear_sample);
-	gs_enable_blending(false);
+		const char *tech_name = "Draw";
+		float multiplier = 1.f;
+		switch (gs_get_color_space()) {
+		case GS_CS_SRGB_16F:
+		case GS_CS_709_EXTENDED:
+			if (!linear_sample)
+				tech_name = "DrawSrgbDecompress";
+			break;
+		case GS_CS_709_SCRGB:
+			if (linear_sample)
+				tech_name = "DrawMultiply";
+			else
+				tech_name = "DrawSrgbDecompressMultiply";
+			multiplier = obs_get_video_sdr_white_level() / 80.f;
+		}
 
-	if (linear_sample)
-		gs_effect_set_texture_srgb(image, texture);
-	else
-		gs_effect_set_texture(image, texture);
+		gs_effect_t *effect = obs_get_base_effect(OBS_EFFECT_OPAQUE);
+		gs_technique_t *tech =
+			gs_effect_get_technique(effect, tech_name);
+		gs_eparam_t *image =
+			gs_effect_get_param_by_name(effect, "image");
 
-	const uint32_t flip = compatibility ? GS_FLIP_V : 0;
-	const size_t passes = gs_technique_begin(tech);
-	for (size_t i = 0; i < passes; i++) {
-		if (gs_technique_begin_pass(tech, i)) {
-			gs_draw_sprite(texture, flip, 0, 0);
+		const bool previous = gs_framebuffer_srgb_enabled();
+		gs_enable_framebuffer_srgb(linear_sample);
+		gs_enable_blending(false);
 
-			gs_technique_end_pass(tech);
-		}
-	}
-	gs_technique_end(tech);
+		if (linear_sample)
+			gs_effect_set_texture_srgb(image, texture);
+		else
+			gs_effect_set_texture(image, texture);
 
-	gs_enable_blending(true);
-	gs_enable_framebuffer_srgb(previous);
-}
+		gs_eparam_t *multiplier_param =
+			gs_effect_get_param_by_name(effect, "multiplier");
+		gs_effect_set_float(multiplier_param, multiplier);
 
-void dc_capture_render(struct dc_capture *capture, bool texcoords_centered)
-{
-	if (capture->valid && capture->texture_written)
-		draw_texture(capture, texcoords_centered);
+		const uint32_t flip = compatibility ? GS_FLIP_V : 0;
+		const size_t passes = gs_technique_begin(tech);
+		for (size_t i = 0; i < passes; i++) {
+			if (gs_technique_begin_pass(tech, i)) {
+				gs_draw_sprite(texture, flip, 0, 0);
+
+				gs_technique_end_pass(tech);
+			}
+		}
+		gs_technique_end(tech);
+
+		gs_enable_blending(true);
+		gs_enable_framebuffer_srgb(previous);
+	}
 }

+ 71 - 5
plugins/win-capture/duplicator-monitor-capture.c

@@ -39,6 +39,8 @@ typedef struct winrt_capture *(*PFN_winrt_capture_init_monitor)(
 typedef void (*PFN_winrt_capture_free)(struct winrt_capture *capture);
 
 typedef BOOL (*PFN_winrt_capture_active)(const struct winrt_capture *capture);
+typedef enum gs_color_space (*PFN_winrt_capture_get_color_space)(
+	const struct winrt_capture *capture);
 typedef void (*PFN_winrt_capture_render)(struct winrt_capture *capture);
 typedef uint32_t (*PFN_winrt_capture_width)(const struct winrt_capture *capture);
 typedef uint32_t (*PFN_winrt_capture_height)(
@@ -51,6 +53,7 @@ struct winrt_exports {
 	PFN_winrt_capture_init_monitor winrt_capture_init_monitor;
 	PFN_winrt_capture_free winrt_capture_free;
 	PFN_winrt_capture_active winrt_capture_active;
+	PFN_winrt_capture_get_color_space winrt_capture_get_color_space;
 	PFN_winrt_capture_render winrt_capture_render;
 	PFN_winrt_capture_width winrt_capture_width;
 	PFN_winrt_capture_height winrt_capture_height;
@@ -299,6 +302,7 @@ static bool load_winrt_imports(struct winrt_exports *exports, void *module,
 	WINRT_IMPORT(winrt_capture_init_monitor);
 	WINRT_IMPORT(winrt_capture_free);
 	WINRT_IMPORT(winrt_capture_active);
+	WINRT_IMPORT(winrt_capture_get_color_space);
 	WINRT_IMPORT(winrt_capture_render);
 	WINRT_IMPORT(winrt_capture_width);
 	WINRT_IMPORT(winrt_capture_height);
@@ -541,13 +545,37 @@ static void duplicator_capture_render(void *data, gs_effect_t *unused)
 			gs_matrix_rotaa4f(0.0f, 0.0f, 1.0f, RAD((float)rot));
 		}
 
+		const char *tech_name = "Draw";
+		float multiplier = 1.f;
+		const enum gs_color_space current_space = gs_get_color_space();
+		if (gs_texture_get_color_format(texture) == GS_RGBA16F) {
+			switch (current_space) {
+			case GS_CS_SRGB:
+			case GS_CS_SRGB_16F:
+				tech_name = "DrawMultiplyTonemap";
+				multiplier =
+					80.f / obs_get_video_sdr_white_level();
+				break;
+			case GS_CS_709_EXTENDED:
+				tech_name = "DrawMultiply";
+				multiplier =
+					80.f / obs_get_video_sdr_white_level();
+			}
+		} else if (current_space == GS_CS_709_SCRGB) {
+			tech_name = "DrawMultiply";
+			multiplier = obs_get_video_sdr_white_level() / 80.f;
+		}
+
 		gs_effect_t *const opaque_effect =
 			obs_get_base_effect(OBS_EFFECT_OPAQUE);
-		while (gs_effect_loop(opaque_effect, "Draw")) {
-			gs_eparam_t *image = gs_effect_get_param_by_name(
-				opaque_effect, "image");
-			gs_effect_set_texture_srgb(image, texture);
-
+		gs_eparam_t *multiplier_param = gs_effect_get_param_by_name(
+			opaque_effect, "multiplier");
+		gs_effect_set_float(multiplier_param, multiplier);
+		gs_eparam_t *image_param =
+			gs_effect_get_param_by_name(opaque_effect, "image");
+		gs_effect_set_texture_srgb(image_param, texture);
+
+		while (gs_effect_loop(opaque_effect, tech_name)) {
 			gs_draw_sprite(texture, 0, 0, 0);
 		}
 
@@ -673,6 +701,43 @@ static obs_properties_t *duplicator_capture_properties(void *data)
 	return props;
 }
 
+enum gs_color_space
+duplicator_capture_get_color_space(void *data, size_t count,
+				   const enum gs_color_space *preferred_spaces)
+{
+	enum gs_color_space capture_space = GS_CS_SRGB;
+
+	struct duplicator_capture *capture = data;
+	if (capture->method == METHOD_WGC) {
+		if (capture->capture_winrt) {
+			capture_space =
+				capture->exports.winrt_capture_get_color_space(
+					capture->capture_winrt);
+		}
+	} else {
+		if (capture->duplicator) {
+			gs_texture_t *const texture =
+				gs_duplicator_get_texture(capture->duplicator);
+			if (texture) {
+				capture_space = (gs_texture_get_color_format(
+							 texture) == GS_RGBA16F)
+							? GS_CS_709_EXTENDED
+							: GS_CS_SRGB;
+			}
+		}
+	}
+
+	enum gs_color_space space = capture_space;
+	for (size_t i = 0; i < count; ++i) {
+		const enum gs_color_space preferred_space = preferred_spaces[i];
+		space = preferred_space;
+		if (preferred_space == capture_space)
+			break;
+	}
+
+	return space;
+}
+
 struct obs_source_info duplicator_capture_info = {
 	.id = "monitor_capture",
 	.type = OBS_SOURCE_TYPE_INPUT,
@@ -689,4 +754,5 @@ struct obs_source_info duplicator_capture_info = {
 	.get_defaults = duplicator_capture_defaults,
 	.get_properties = duplicator_capture_properties,
 	.icon_type = OBS_ICON_TYPE_DESKTOP_CAPTURE,
+	.video_get_color_space = duplicator_capture_get_color_space,
 };

+ 29 - 0
plugins/win-capture/window-capture.c

@@ -37,6 +37,8 @@ typedef void (*PFN_winrt_capture_free)(struct winrt_capture *capture);
 typedef BOOL (*PFN_winrt_capture_active)(const struct winrt_capture *capture);
 typedef BOOL (*PFN_winrt_capture_show_cursor)(struct winrt_capture *capture,
 					      BOOL visible);
+typedef enum gs_color_space (*PFN_winrt_capture_get_color_space)(
+	const struct winrt_capture *capture);
 typedef void (*PFN_winrt_capture_render)(struct winrt_capture *capture);
 typedef uint32_t (*PFN_winrt_capture_width)(const struct winrt_capture *capture);
 typedef uint32_t (*PFN_winrt_capture_height)(
@@ -50,6 +52,7 @@ struct winrt_exports {
 	PFN_winrt_capture_free winrt_capture_free;
 	PFN_winrt_capture_active winrt_capture_active;
 	PFN_winrt_capture_show_cursor winrt_capture_show_cursor;
+	PFN_winrt_capture_get_color_space winrt_capture_get_color_space;
 	PFN_winrt_capture_render winrt_capture_render;
 	PFN_winrt_capture_width winrt_capture_width;
 	PFN_winrt_capture_height winrt_capture_height;
@@ -238,6 +241,7 @@ static bool load_winrt_imports(struct winrt_exports *exports, void *module,
 	WINRT_IMPORT(winrt_capture_free);
 	WINRT_IMPORT(winrt_capture_active);
 	WINRT_IMPORT(winrt_capture_show_cursor);
+	WINRT_IMPORT(winrt_capture_get_color_space);
 	WINRT_IMPORT(winrt_capture_render);
 	WINRT_IMPORT(winrt_capture_width);
 	WINRT_IMPORT(winrt_capture_height);
@@ -644,6 +648,30 @@ static void wc_render(void *data, gs_effect_t *effect)
 	UNUSED_PARAMETER(effect);
 }
 
+enum gs_color_space
+wc_get_color_space(void *data, size_t count,
+		   const enum gs_color_space *preferred_spaces)
+{
+	struct window_capture *wc = data;
+
+	enum gs_color_space capture_space = GS_CS_SRGB;
+
+	if ((wc->method == METHOD_WGC) && wc->capture_winrt) {
+		capture_space = wc->exports.winrt_capture_get_color_space(
+			wc->capture_winrt);
+	}
+
+	enum gs_color_space space = capture_space;
+	for (size_t i = 0; i < count; ++i) {
+		const enum gs_color_space preferred_space = preferred_spaces[i];
+		space = preferred_space;
+		if (preferred_space == capture_space)
+			break;
+	}
+
+	return space;
+}
+
 struct obs_source_info window_capture_info = {
 	.id = "window_capture",
 	.type = OBS_SOURCE_TYPE_INPUT,
@@ -661,4 +689,5 @@ struct obs_source_info window_capture_info = {
 	.get_defaults = wc_defaults,
 	.get_properties = wc_properties,
 	.icon_type = OBS_ICON_TYPE_WINDOW_CAPTURE,
+	.video_get_color_space = wc_get_color_space,
 };