1
0
Эх сурвалжийг харах

Merge pull request #4950 from jpark37/manual-srgb-filtering

Manual SRGB filtering
Jim 4 жил өмнө
parent
commit
4fad39cdc4

+ 8 - 0
docs/sphinx/reference-sources.rst

@@ -847,6 +847,14 @@ General Source Functions
 
 ---------------------
 
+.. function:: bool obs_source_get_texcoords_centered(obs_source_t *source)
+
+   Hints whether or not the source will blend texels.
+
+   :return: Whether or not the source will blend texels
+
+---------------------
+
 .. function:: obs_data_t *obs_source_get_settings(const obs_source_t *source)
 
    :return: The settings string for a source.  The reference counter of the

+ 5 - 2
libobs-d3d11/d3d11-duplicator.cpp

@@ -218,9 +218,12 @@ static inline void copy_texture(gs_duplicator_t *d, ID3D11Texture2D *tex)
 	    d->texture->height != desc.Height) {
 
 		delete d->texture;
+		const gs_color_format format =
+			ConvertDXGITextureFormat(desc.Format);
+		const gs_color_format srgb_format =
+			gs_generalize_format(format);
 		d->texture = (gs_texture_2d *)gs_texture_create(
-			desc.Width, desc.Height,
-			ConvertDXGITextureFormat(desc.Format), 1, nullptr, 0);
+			desc.Width, desc.Height, srgb_format, 1, nullptr, 0);
 	}
 
 	if (!!d->texture)

+ 16 - 1
libobs-d3d11/d3d11-subsystem.cpp

@@ -1768,6 +1768,20 @@ inline void gs_device::CopyTex(ID3D11Texture2D *dst, uint32_t dst_x,
 	}
 }
 
+static DXGI_FORMAT get_copy_compare_format(gs_color_format format)
+{
+	switch (format) {
+	case GS_RGBA_UNORM:
+		return DXGI_FORMAT_R8G8B8A8_TYPELESS;
+	case GS_BGRX_UNORM:
+		return DXGI_FORMAT_B8G8R8X8_TYPELESS;
+	case GS_BGRA_UNORM:
+		return DXGI_FORMAT_B8G8R8A8_TYPELESS;
+	default:
+		return ConvertGSTextureFormatResource(format);
+	}
+}
+
 void device_copy_texture_region(gs_device_t *device, gs_texture_t *dst,
 				uint32_t dst_x, uint32_t dst_y,
 				gs_texture_t *src, uint32_t src_x,
@@ -1784,7 +1798,8 @@ void device_copy_texture_region(gs_device_t *device, gs_texture_t *dst,
 		if (src->type != GS_TEXTURE_2D || dst->type != GS_TEXTURE_2D)
 			throw "Source and destination textures must be a 2D "
 			      "textures";
-		if (dst->format != src->format)
+		if (get_copy_compare_format(dst->format) !=
+		    get_copy_compare_format(src->format))
 			throw "Source and destination formats do not match";
 
 		/* apparently casting to the same type that the variable

+ 1 - 1
libobs-opengl/gl-subsystem.h

@@ -150,7 +150,7 @@ static inline GLenum get_gl_format_type(enum gs_color_format format)
 	case GS_BGRA:
 		return GL_UNSIGNED_BYTE;
 	case GS_R10G10B10A2:
-		return GL_UNSIGNED_INT_10_10_10_2;
+		return GL_UNSIGNED_INT_2_10_10_10_REV;
 	case GS_RGBA16:
 		return GL_UNSIGNED_SHORT;
 	case GS_R16:

+ 10 - 14
libobs-winrt/winrt-capture.cpp

@@ -564,24 +564,20 @@ extern "C" EXPORT void winrt_capture_free(struct winrt_capture *capture)
 	}
 }
 
-static void draw_texture(struct winrt_capture *capture, gs_effect_t *effect)
+static void draw_texture(struct winrt_capture *capture)
 {
-	gs_texture_t *const texture = capture->texture;
+	gs_effect_t *const 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");
-	size_t passes;
-
-	const bool linear_srgb = gs_get_linear_srgb();
 
 	const bool previous = gs_framebuffer_srgb_enabled();
-	gs_enable_framebuffer_srgb(linear_srgb);
+	gs_enable_framebuffer_srgb(true);
+	gs_enable_blending(false);
 
-	if (linear_srgb)
-		gs_effect_set_texture_srgb(image, texture);
-	else
-		gs_effect_set_texture(image, texture);
+	gs_texture_t *const texture = capture->texture;
+	gs_effect_set_texture_srgb(image, texture);
 
-	passes = gs_technique_begin(tech);
+	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, 0, 0, 0);
@@ -591,6 +587,7 @@ static void draw_texture(struct winrt_capture *capture, gs_effect_t *effect)
 	}
 	gs_technique_end(tech);
 
+	gs_enable_blending(true);
 	gs_enable_framebuffer_srgb(previous);
 }
 
@@ -627,11 +624,10 @@ extern "C" EXPORT BOOL winrt_capture_show_cursor(struct winrt_capture *capture,
 	return success;
 }
 
-extern "C" EXPORT void winrt_capture_render(struct winrt_capture *capture,
-					    gs_effect_t *effect)
+extern "C" EXPORT void winrt_capture_render(struct winrt_capture *capture)
 {
 	if (capture->texture_written)
-		draw_texture(capture, effect);
+		draw_texture(capture);
 }
 
 extern "C" EXPORT uint32_t

+ 1 - 2
libobs-winrt/winrt-capture.h

@@ -20,8 +20,7 @@ EXPORT void winrt_capture_free(struct winrt_capture *capture);
 EXPORT BOOL winrt_capture_active(const struct winrt_capture *capture);
 EXPORT BOOL winrt_capture_show_cursor(struct winrt_capture *capture,
 				      BOOL visible);
-EXPORT void winrt_capture_render(struct winrt_capture *capture,
-				 gs_effect_t *effect);
+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);
 

+ 16 - 0
libobs/data/default.effect

@@ -62,6 +62,13 @@ float4 PSDrawNonlinearAlpha(VertInOut vert_in) : TARGET
 	return rgba;
 }
 
+float4 PSDrawSrgbDecompress(VertInOut vert_in) : TARGET
+{
+	float4 rgba = image.Sample(def_sampler, vert_in.uv);
+	rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb);
+	return rgba;
+}
+
 float4 PSDrawSrgbDecompressPremultiplied(VertInOut vert_in) : TARGET
 {
 	float4 rgba = image.Sample(def_sampler, vert_in.uv);
@@ -97,6 +104,15 @@ technique DrawNonlinearAlpha
 	}
 }
 
+technique DrawSrgbDecompress
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSDrawSrgbDecompress(vert_in);
+	}
+}
+
 technique DrawSrgbDecompressPremultiplied
 {
 	pass

+ 18 - 0
libobs/graphics/graphics.h

@@ -1005,6 +1005,24 @@ static inline bool gs_is_srgb_format(enum gs_color_format format)
 	}
 }
 
+static inline enum gs_color_format
+gs_generalize_format(enum gs_color_format format)
+{
+	switch (format) {
+	case GS_RGBA_UNORM:
+		format = GS_RGBA;
+		break;
+	case GS_BGRX_UNORM:
+		format = GS_BGRX;
+		break;
+	case GS_BGRA_UNORM:
+		format = GS_BGRA;
+	default:;
+	}
+
+	return format;
+}
+
 static inline uint32_t gs_get_total_levels(uint32_t width, uint32_t height,
 					   uint32_t depth)
 {

+ 5 - 0
libobs/obs-internal.h

@@ -630,6 +630,9 @@ struct obs_source {
 	/* used to temporarily disable sources if needed */
 	bool enabled;
 
+	/* hint to allow sources to render more quickly */
+	bool texcoords_centered;
+
 	/* timing (if video is present, is based upon video) */
 	volatile bool timing_set;
 	volatile uint64_t timing_adjust;
@@ -847,6 +850,8 @@ convert_video_format(enum video_format format)
 	}
 }
 
+extern void obs_source_set_texcoords_centered(obs_source_t *source,
+					      bool centered);
 extern void obs_source_activate(obs_source_t *source, enum view_type type);
 extern void obs_source_deactivate(obs_source_t *source, enum view_type type);
 extern void obs_source_video_tick(obs_source_t *source, float seconds);

+ 22 - 0
libobs/obs-scene.c

@@ -561,6 +561,20 @@ static void render_item_texture(struct obs_scene_item *item)
 	GS_DEBUG_MARKER_END();
 }
 
+static bool are_texcoords_centered(struct matrix4 *m)
+{
+	static const struct matrix4 identity = {
+		{1.0f, 0.0f, 0.0f, 0.0f},
+		{0.0f, 1.0f, 0.0f, 0.0f},
+		{0.0f, 0.0f, 1.0f, 0.0f},
+		{0.0f, 0.0f, 0.0f, 1.0f},
+	};
+	struct matrix4 copy = identity;
+	copy.t.x = floorf(m->t.x);
+	copy.t.y = floorf(m->t.y);
+	return memcmp(m, &copy, sizeof(*m)) == 0;
+}
+
 static inline void render_item(struct obs_scene_item *item)
 {
 	GS_DEBUG_MARKER_BEGIN_FORMAT(GS_DEBUG_COLOR_ITEM, "Item: %s",
@@ -610,7 +624,11 @@ static inline void render_item(struct obs_scene_item *item)
 							cx, cy);
 				obs_source_video_render(item->hide_transition);
 			} else {
+				obs_source_set_texcoords_centered(item->source,
+								  true);
 				obs_source_video_render(item->source);
+				obs_source_set_texcoords_centered(item->source,
+								  false);
 			}
 
 			gs_texrender_end(item->item_render);
@@ -635,7 +653,11 @@ static inline void render_item(struct obs_scene_item *item)
 		obs_transition_set_size(item->hide_transition, cx, cy);
 		obs_source_video_render(item->hide_transition);
 	} else {
+		const bool centered =
+			are_texcoords_centered(&item->draw_transform);
+		obs_source_set_texcoords_centered(item->source, centered);
 		obs_source_video_render(item->source);
+		obs_source_set_texcoords_centered(item->source, false);
 	}
 	gs_matrix_pop();
 	gs_set_linear_srgb(previous);

+ 10 - 0
libobs/obs-source.c

@@ -1030,6 +1030,16 @@ void obs_source_send_key_click(obs_source_t *source,
 	}
 }
 
+bool obs_source_get_texcoords_centered(obs_source_t *source)
+{
+	return source->texcoords_centered;
+}
+
+void obs_source_set_texcoords_centered(obs_source_t *source, bool centered)
+{
+	source->texcoords_centered = centered;
+}
+
 static void activate_source(obs_source_t *source)
 {
 	if (source->context.data && source->info.activate)

+ 3 - 0
libobs/obs.h

@@ -962,6 +962,9 @@ EXPORT uint32_t obs_source_get_width(obs_source_t *source);
 /** Gets the height of a source (if it has video) */
 EXPORT uint32_t obs_source_get_height(obs_source_t *source);
 
+/** Hints whether or not the source will blend texels */
+EXPORT bool obs_source_get_texcoords_centered(obs_source_t *source);
+
 /**
  * If the source is a filter, returns the parent source of the filter.  Only
  * guaranteed to be valid inside of the video_render, filter_audio,

+ 36 - 15
plugins/win-capture/dc-capture.c

@@ -5,14 +5,27 @@
 
 static inline void init_textures(struct dc_capture *capture)
 {
-	if (capture->compatibility)
+	if (capture->compatibility) {
 		capture->texture = gs_texture_create(capture->width,
 						     capture->height, GS_BGRA,
 						     1, NULL, GS_DYNAMIC);
-	else
+	} else {
 		capture->texture =
 			gs_texture_create_gdi(capture->width, capture->height);
 
+		if (capture->texture) {
+			capture->extra_texture = gs_texture_create(
+				capture->width, capture->height, GS_BGRA, 1,
+				NULL, 0);
+			if (!capture->extra_texture) {
+				blog(LOG_WARNING, "[dc_capture_init] Failed to "
+						  "create textures");
+				gs_texture_destroy(capture->texture);
+				capture->texture = NULL;
+			}
+		}
+	}
+
 	if (!capture->texture) {
 		blog(LOG_WARNING, "[dc_capture_init] Failed to "
 				  "create textures");
@@ -73,6 +86,7 @@ void dc_capture_free(struct dc_capture *capture)
 	}
 
 	obs_enter_graphics();
+	gs_texture_destroy(capture->extra_texture);
 	gs_texture_destroy(capture->texture);
 	obs_leave_graphics();
 
@@ -165,41 +179,48 @@ void dc_capture_capture(struct dc_capture *capture, HWND window)
 	capture->texture_written = true;
 }
 
-static void draw_texture(struct dc_capture *capture, gs_effect_t *effect)
+static void draw_texture(struct dc_capture *capture, bool texcoords_centered)
 {
-	gs_texture_t *texture = capture->texture;
+	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");
-	size_t passes;
 
-	const bool linear_srgb = gs_get_linear_srgb() && capture->compatibility;
+	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_srgb);
+	gs_enable_framebuffer_srgb(linear_sample);
+	gs_enable_blending(false);
 
-	if (linear_srgb)
+	if (linear_sample)
 		gs_effect_set_texture_srgb(image, texture);
 	else
 		gs_effect_set_texture(image, texture);
 
-	passes = gs_technique_begin(tech);
+	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)) {
-			if (capture->compatibility)
-				gs_draw_sprite(texture, GS_FLIP_V, 0, 0);
-			else
-				gs_draw_sprite(texture, 0, 0, 0);
+			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);
 }
 
-void dc_capture_render(struct dc_capture *capture, gs_effect_t *effect)
+void dc_capture_render(struct dc_capture *capture, bool texcoords_centered)
 {
 	if (capture->valid && capture->texture_written)
-		draw_texture(capture, effect);
+		draw_texture(capture, texcoords_centered);
 }

+ 3 - 1
plugins/win-capture/dc-capture.h

@@ -7,6 +7,7 @@
 
 struct dc_capture {
 	gs_texture_t *texture;
+	gs_texture_t *extra_texture;
 	bool texture_written;
 	int x, y;
 	uint32_t width;
@@ -31,4 +32,5 @@ extern void dc_capture_init(struct dc_capture *capture, int x, int y,
 extern void dc_capture_free(struct dc_capture *capture);
 
 extern void dc_capture_capture(struct dc_capture *capture, HWND window);
-extern void dc_capture_render(struct dc_capture *capture, gs_effect_t *effect);
+extern void dc_capture_render(struct dc_capture *capture,
+			      bool texcoords_centered);

+ 49 - 42
plugins/win-capture/duplicator-monitor-capture.c

@@ -39,8 +39,7 @@ 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 void (*PFN_winrt_capture_render)(struct winrt_capture *capture,
-					 gs_effect_t *effect);
+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)(
 	const struct winrt_capture *capture);
@@ -488,18 +487,18 @@ static void draw_cursor(struct duplicator_capture *capture)
 		    capture->rot % 180 == 0 ? capture->height : capture->width);
 }
 
-static void duplicator_capture_render(void *data, gs_effect_t *effect)
+static void duplicator_capture_render(void *data, gs_effect_t *unused)
 {
+	UNUSED_PARAMETER(unused);
+
 	struct duplicator_capture *capture = data;
 
 	if (capture->method == METHOD_WGC) {
-		gs_effect_t *const opaque =
-			obs_get_base_effect(OBS_EFFECT_OPAQUE);
 		if (capture->capture_winrt) {
 			if (capture->exports.winrt_capture_active(
 				    capture->capture_winrt)) {
 				capture->exports.winrt_capture_render(
-					capture->capture_winrt, opaque);
+					capture->capture_winrt);
 			} else {
 				capture->exports.winrt_capture_free(
 					capture->capture_winrt);
@@ -507,54 +506,62 @@ static void duplicator_capture_render(void *data, gs_effect_t *effect)
 			}
 		}
 	} else {
-		gs_texture_t *texture;
-		int rot;
-
 		if (!capture->duplicator)
 			return;
 
-		texture = gs_duplicator_get_texture(capture->duplicator);
+		gs_texture_t *const texture =
+			gs_duplicator_get_texture(capture->duplicator);
 		if (!texture)
 			return;
 
-		effect = obs_get_base_effect(OBS_EFFECT_OPAQUE);
-
-		rot = capture->rot;
-
-		while (gs_effect_loop(effect, "Draw")) {
-			if (rot != 0) {
-				float x = 0.0f;
-				float y = 0.0f;
-
-				switch (rot) {
-				case 90:
-					x = (float)capture->height;
-					break;
-				case 180:
-					x = (float)capture->width;
-					y = (float)capture->height;
-					break;
-				case 270:
-					y = (float)capture->width;
-					break;
-				}
-
-				gs_matrix_push();
-				gs_matrix_translate3f(x, y, 0.0f);
-				gs_matrix_rotaa4f(0.0f, 0.0f, 1.0f,
-						  RAD((float)rot));
+		const bool previous = gs_framebuffer_srgb_enabled();
+		gs_enable_framebuffer_srgb(true);
+		gs_enable_blending(false);
+
+		const int rot = capture->rot;
+		if (rot != 0) {
+			float x = 0.0f;
+			float y = 0.0f;
+
+			switch (rot) {
+			case 90:
+				x = (float)capture->height;
+				break;
+			case 180:
+				x = (float)capture->width;
+				y = (float)capture->height;
+				break;
+			case 270:
+				y = (float)capture->width;
+				break;
 			}
 
-			obs_source_draw(texture, 0, 0, 0, 0, false);
+			gs_matrix_push();
+			gs_matrix_translate3f(x, y, 0.0f);
+			gs_matrix_rotaa4f(0.0f, 0.0f, 1.0f, RAD((float)rot));
+		}
+
+		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);
 
-			if (rot != 0)
-				gs_matrix_pop();
+			gs_draw_sprite(texture, 0, 0, 0);
 		}
 
+		if (rot != 0)
+			gs_matrix_pop();
+
+		gs_enable_blending(true);
+		gs_enable_framebuffer_srgb(previous);
+
 		if (capture->capture_cursor) {
-			effect = obs_get_base_effect(OBS_EFFECT_DEFAULT);
+			gs_effect_t *const default_effect =
+				obs_get_base_effect(OBS_EFFECT_DEFAULT);
 
-			while (gs_effect_loop(effect, "Draw")) {
+			while (gs_effect_loop(default_effect, "Draw")) {
 				draw_cursor(capture);
 			}
 		}
@@ -670,7 +677,7 @@ struct obs_source_info duplicator_capture_info = {
 	.id = "monitor_capture",
 	.type = OBS_SOURCE_TYPE_INPUT,
 	.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW |
-			OBS_SOURCE_DO_NOT_DUPLICATE,
+			OBS_SOURCE_DO_NOT_DUPLICATE | OBS_SOURCE_SRGB,
 	.get_name = duplicator_capture_getname,
 	.create = duplicator_capture_create,
 	.destroy = duplicator_capture_destroy,

+ 156 - 47
plugins/win-capture/game-capture.c

@@ -149,7 +149,9 @@ struct game_capture {
 
 	ipc_pipe_server_t pipe;
 	gs_texture_t *texture;
-	bool supports_srgb;
+	gs_texture_t *extra_texture;
+	gs_texrender_t *extra_texrender;
+	bool linear_sample;
 	struct hook_info *global_hook_info;
 	HANDLE keepalive_mutex;
 	HANDLE hook_init;
@@ -332,12 +334,14 @@ static void stop_capture(struct game_capture *gc)
 	close_handle(&gc->texture_mutexes[0]);
 	close_handle(&gc->texture_mutexes[1]);
 
-	if (gc->texture) {
-		obs_enter_graphics();
-		gs_texture_destroy(gc->texture);
-		obs_leave_graphics();
-		gc->texture = NULL;
-	}
+	obs_enter_graphics();
+	gs_texrender_destroy(gc->extra_texrender);
+	gs_texture_destroy(gc->extra_texture);
+	gs_texture_destroy(gc->texture);
+	obs_leave_graphics();
+	gc->extra_texrender = NULL;
+	gc->extra_texture = NULL;
+	gc->texture = NULL;
 
 	if (gc->active)
 		info("capture stopped");
@@ -1552,49 +1556,104 @@ static inline bool is_16bit_format(uint32_t format)
 
 static inline bool init_shmem_capture(struct game_capture *gc)
 {
-	enum gs_color_format format;
-
-	gc->texture_buffers[0] =
-		(uint8_t *)gc->data + gc->shmem_data->tex1_offset;
-	gc->texture_buffers[1] =
-		(uint8_t *)gc->data + gc->shmem_data->tex2_offset;
-
-	gc->convert_16bit = is_16bit_format(gc->global_hook_info->format);
-	format = gc->convert_16bit
-			 ? GS_BGRA
-			 : convert_format(gc->global_hook_info->format);
+	const uint32_t dxgi_format = gc->global_hook_info->format;
+	const bool convert_16bit = is_16bit_format(dxgi_format);
+	const enum gs_color_format format =
+		convert_16bit ? GS_BGRA : convert_format(dxgi_format);
 
 	obs_enter_graphics();
+	gs_texrender_destroy(gc->extra_texrender);
+	gs_texture_destroy(gc->extra_texture);
 	gs_texture_destroy(gc->texture);
-	gc->texture =
+	gs_texture_t *const texture =
 		gs_texture_create(gc->cx, gc->cy, format, 1, NULL, GS_DYNAMIC);
 	obs_leave_graphics();
 
-	if (!gc->texture) {
+	bool success = texture != NULL;
+	if (success) {
+		const bool linear_sample = format != GS_R10G10B10A2;
+
+		gs_texrender_t *extra_texrender = NULL;
+		if (!linear_sample) {
+			extra_texrender =
+				gs_texrender_create(GS_BGRA, GS_ZS_NONE);
+			success = extra_texrender != NULL;
+			if (!success)
+				warn("init_shmem_capture: failed to create extra texrender");
+		}
+
+		if (success) {
+			gc->texture_buffers[0] = (uint8_t *)gc->data +
+						 gc->shmem_data->tex1_offset;
+			gc->texture_buffers[1] = (uint8_t *)gc->data +
+						 gc->shmem_data->tex2_offset;
+			gc->convert_16bit = convert_16bit;
+
+			gc->texture = texture;
+			gc->extra_texture = NULL;
+			gc->extra_texrender = extra_texrender;
+			gc->linear_sample = linear_sample;
+			gc->copy_texture = copy_shmem_tex;
+		} else {
+			gs_texture_destroy(texture);
+		}
+	} else {
 		warn("init_shmem_capture: failed to create texture");
-		return false;
 	}
 
-	gc->supports_srgb = true;
-	gc->copy_texture = copy_shmem_tex;
-	return true;
+	return success;
 }
 
 static inline bool init_shtex_capture(struct game_capture *gc)
 {
 	obs_enter_graphics();
+	gs_texrender_destroy(gc->extra_texrender);
+	gs_texture_destroy(gc->extra_texture);
 	gs_texture_destroy(gc->texture);
-	gc->texture = gs_texture_open_shared(gc->shtex_data->tex_handle);
-	enum gs_color_format format = gs_texture_get_color_format(gc->texture);
-	gc->supports_srgb = gs_is_srgb_format(format);
-	obs_leave_graphics();
+	gs_texture_t *const texture =
+		gs_texture_open_shared(gc->shtex_data->tex_handle);
+	bool success = texture != NULL;
+	if (success) {
+		enum gs_color_format format =
+			gs_texture_get_color_format(texture);
+		const bool ten_bit_srgb = (format == GS_R10G10B10A2);
+		enum gs_color_format linear_format =
+			ten_bit_srgb ? GS_BGRA : gs_generalize_format(format);
+		const bool linear_sample = (linear_format == format);
+		gs_texture_t *extra_texture = NULL;
+		gs_texrender_t *extra_texrender = NULL;
+		if (!linear_sample) {
+			if (ten_bit_srgb) {
+				extra_texrender = gs_texrender_create(
+					linear_format, GS_ZS_NONE);
+				success = extra_texrender != NULL;
+				if (!success)
+					warn("init_shtex_capture: failed to create extra texrender");
+			} else {
+				extra_texture = gs_texture_create(
+					gs_texture_get_width(texture),
+					gs_texture_get_height(texture),
+					linear_format, 1, NULL, 0);
+				success = extra_texture != NULL;
+				if (!success)
+					warn("init_shtex_capture: failed to create extra texture");
+			}
+		}
 
-	if (!gc->texture) {
+		if (success) {
+			gc->texture = texture;
+			gc->linear_sample = linear_sample;
+			gc->extra_texture = extra_texture;
+			gc->extra_texrender = extra_texrender;
+		} else {
+			gs_texture_destroy(texture);
+		}
+	} else {
 		warn("init_shtex_capture: failed to open shared handle");
-		return false;
 	}
+	obs_leave_graphics();
 
-	return true;
+	return success;
 }
 
 static bool start_capture(struct game_capture *gc)
@@ -1802,36 +1861,86 @@ static inline void game_capture_render_cursor(struct game_capture *gc)
 		    gc->global_hook_info->cy);
 }
 
-static void game_capture_render(void *data, gs_effect_t *effect)
+static void game_capture_render(void *data, gs_effect_t *unused)
 {
+	UNUSED_PARAMETER(unused);
+
 	struct game_capture *gc = data;
 	if (!gc->texture || !gc->active)
 		return;
 
-	effect = obs_get_base_effect(gc->config.allow_transparency
-					     ? OBS_EFFECT_DEFAULT
-					     : OBS_EFFECT_OPAQUE);
+	const bool allow_transparency = gc->config.allow_transparency;
+	gs_effect_t *const effect = obs_get_base_effect(
+		allow_transparency ? OBS_EFFECT_DEFAULT : OBS_EFFECT_OPAQUE);
+
+	bool linear_sample = gc->linear_sample;
+	gs_texture_t *texture = gc->texture;
+	if (!linear_sample && !obs_source_get_texcoords_centered(gc->source)) {
+		gs_texture_t *const extra_texture = gc->extra_texture;
+		if (extra_texture) {
+			gs_copy_texture(extra_texture, texture);
+			texture = extra_texture;
+		} else {
+			gs_texrender_t *const texrender = gc->extra_texrender;
+			gs_texrender_reset(texrender);
+			const uint32_t cx = gs_texture_get_width(texture);
+			const uint32_t cy = gs_texture_get_height(texture);
+			if (gs_texrender_begin(texrender, cx, cy)) {
+				gs_effect_t *const default_effect =
+					obs_get_base_effect(OBS_EFFECT_DEFAULT);
+				const bool previous =
+					gs_framebuffer_srgb_enabled();
+				gs_enable_framebuffer_srgb(false);
+				gs_enable_blending(false);
+				gs_ortho(0.0f, (float)cx, 0.0f, (float)cy,
+					 -100.0f, 100.0f);
+				gs_eparam_t *const image =
+					gs_effect_get_param_by_name(
+						default_effect, "image");
+				gs_effect_set_texture(image, texture);
+				while (gs_effect_loop(default_effect, "Draw")) {
+					gs_draw_sprite(texture, 0, 0, 0);
+				}
+				gs_enable_blending(true);
+				gs_enable_framebuffer_srgb(previous);
+
+				gs_texrender_end(texrender);
+
+				texture = gs_texrender_get_texture(texrender);
+			}
+		}
 
-	const bool linear_srgb = gs_get_linear_srgb() && gc->supports_srgb;
-	const bool previous = gs_set_linear_srgb(linear_srgb);
+		linear_sample = true;
+	}
 
-	while (gs_effect_loop(effect, "Draw")) {
-		obs_source_draw(gc->texture, 0, 0, 0, 0,
-				gc->global_hook_info->flip);
+	gs_eparam_t *const image = gs_effect_get_param_by_name(effect, "image");
+	const uint32_t flip = gc->global_hook_info->flip ? GS_FLIP_V : 0;
+	const char *tech_name = allow_transparency && !linear_sample
+					? "DrawSrgbDecompress"
+					: "Draw";
+	while (gs_effect_loop(effect, tech_name)) {
+		const bool previous = gs_framebuffer_srgb_enabled();
+		gs_enable_framebuffer_srgb(allow_transparency || linear_sample);
+		gs_enable_blending(allow_transparency);
+		if (linear_sample)
+			gs_effect_set_texture_srgb(image, texture);
+		else
+			gs_effect_set_texture(image, texture);
+		gs_draw_sprite(texture, flip, 0, 0);
+		gs_enable_blending(true);
+		gs_enable_framebuffer_srgb(previous);
 
-		if (gc->config.allow_transparency && gc->config.cursor &&
+		if (allow_transparency && gc->config.cursor &&
 		    !gc->cursor_hidden) {
 			game_capture_render_cursor(gc);
 		}
 	}
 
-	gs_set_linear_srgb(previous);
-
-	if (!gc->config.allow_transparency && gc->config.cursor &&
-	    !gc->cursor_hidden) {
-		effect = obs_get_base_effect(OBS_EFFECT_DEFAULT);
+	if (!allow_transparency && gc->config.cursor && !gc->cursor_hidden) {
+		gs_effect_t *const default_effect =
+			obs_get_base_effect(OBS_EFFECT_DEFAULT);
 
-		while (gs_effect_loop(effect, "Draw")) {
+		while (gs_effect_loop(default_effect, "Draw")) {
 			game_capture_render_cursor(gc);
 		}
 	}

+ 1 - 1
plugins/win-capture/monitor-capture.c

@@ -152,7 +152,7 @@ static void monitor_capture_render(void *data, gs_effect_t *effect)
 {
 	struct monitor_capture *capture = data;
 	dc_capture_render(&capture->data,
-			  obs_get_base_effect(OBS_EFFECT_OPAQUE));
+			  obs_source_get_texcoords_centered(capture->source));
 
 	UNUSED_PARAMETER(effect);
 }

+ 5 - 5
plugins/win-capture/window-capture.c

@@ -37,8 +37,7 @@ 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 void (*PFN_winrt_capture_render)(struct winrt_capture *capture,
-					 gs_effect_t *effect);
+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)(
 	const struct winrt_capture *capture);
@@ -574,13 +573,12 @@ static void wc_tick(void *data, float seconds)
 static void wc_render(void *data, gs_effect_t *effect)
 {
 	struct window_capture *wc = data;
-	gs_effect_t *const opaque = obs_get_base_effect(OBS_EFFECT_OPAQUE);
 	if (wc->method == METHOD_WGC) {
 		if (wc->capture_winrt) {
 			if (wc->exports.winrt_capture_active(
 				    wc->capture_winrt)) {
 				wc->exports.winrt_capture_render(
-					wc->capture_winrt, opaque);
+					wc->capture_winrt);
 			} else {
 				wc->exports.winrt_capture_free(
 					wc->capture_winrt);
@@ -588,7 +586,9 @@ static void wc_render(void *data, gs_effect_t *effect)
 			}
 		}
 	} else {
-		dc_capture_render(&wc->capture, opaque);
+		dc_capture_render(
+			&wc->capture,
+			obs_source_get_texcoords_centered(wc->source));
 	}
 
 	UNUSED_PARAMETER(effect);