From c9766d8e286c4dc1e936fb86d7c2da8b36ed7553 Mon Sep 17 00:00:00 2001 From: jpark37 Date: Tue, 6 Jul 2021 21:46:13 -0700 Subject: [PATCH 1/7] libobs: Add DrawSrgbDecompress default technique Useful when the texture does not support SRGB conversion on load. --- libobs/data/default.effect | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/libobs/data/default.effect b/libobs/data/default.effect index bc343ad14..86d4c6d82 100644 --- a/libobs/data/default.effect +++ b/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 From da3375d5a4ce7a6ae172b4f922e08b40b37ef02d Mon Sep 17 00:00:00 2001 From: jpark37 Date: Tue, 6 Jul 2021 21:59:10 -0700 Subject: [PATCH 2/7] libobs: Add gs_generalize_format helper --- libobs/graphics/graphics.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/libobs/graphics/graphics.h b/libobs/graphics/graphics.h index b43bd7b15..49cf12d62 100644 --- a/libobs/graphics/graphics.h +++ b/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) { From 7c72fd1d4c10386b6c086dba61fed63256593eeb Mon Sep 17 00:00:00 2001 From: jpark37 Date: Tue, 6 Jul 2021 22:03:45 -0700 Subject: [PATCH 3/7] libobs: Plumb texcoord hint to reduce GPU cost In order to do linear-correct filtering cheaply when scale filtering is disabled, we need to know whether or not texture coordinates will always sample from texel centers. This can be computed at the scene item level relatively easily, and passed along to sources when rendering. Scene items will use obs_source_set_texcoords_centered to set hint status, and sources will use obs_source_get_texcoords_centered to retrieve it. --- docs/sphinx/reference-sources.rst | 8 ++++++++ libobs/obs-internal.h | 5 +++++ libobs/obs-scene.c | 22 ++++++++++++++++++++++ libobs/obs-source.c | 10 ++++++++++ libobs/obs.h | 3 +++ 5 files changed, 48 insertions(+) diff --git a/docs/sphinx/reference-sources.rst b/docs/sphinx/reference-sources.rst index d752039c1..456470340 100644 --- a/docs/sphinx/reference-sources.rst +++ b/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 diff --git a/libobs/obs-internal.h b/libobs/obs-internal.h index af4b22fdb..f091ffa6a 100644 --- a/libobs/obs-internal.h +++ b/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); diff --git a/libobs/obs-scene.c b/libobs/obs-scene.c index 554503004..fab6f4b36 100644 --- a/libobs/obs-scene.c +++ b/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, ©, 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); diff --git a/libobs/obs-source.c b/libobs/obs-source.c index 50c7deed1..93cd1aa29 100644 --- a/libobs/obs-source.c +++ b/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) diff --git a/libobs/obs.h b/libobs/obs.h index f74a2f596..30f6d11af 100644 --- a/libobs/obs.h +++ b/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, From 6d59cf19e9e4652b65227a1fd5996ca79dd33491 Mon Sep 17 00:00:00 2001 From: jpark37 Date: Tue, 6 Jul 2021 22:08:18 -0700 Subject: [PATCH 4/7] libobs-d3d11: Use typeless texture for duplicator This allows us to use an SRGB SRV for automatic decompression. --- libobs-d3d11/d3d11-duplicator.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libobs-d3d11/d3d11-duplicator.cpp b/libobs-d3d11/d3d11-duplicator.cpp index e2968e797..ac8004c65 100644 --- a/libobs-d3d11/d3d11-duplicator.cpp +++ b/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) From 993c46c8a2e74f34454daf58fec58e1636e557c5 Mon Sep 17 00:00:00 2001 From: jpark37 Date: Tue, 6 Jul 2021 22:09:38 -0700 Subject: [PATCH 5/7] libobs-d3d11: Relax texture format copy check SRGB and non-SRGB formats are compatible for copy. --- libobs-d3d11/d3d11-subsystem.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/libobs-d3d11/d3d11-subsystem.cpp b/libobs-d3d11/d3d11-subsystem.cpp index 4c2b60258..e231c3538 100644 --- a/libobs-d3d11/d3d11-subsystem.cpp +++ b/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 From ebfbe1a78e5e8ca15cb8f9c09db5e7e034f0721d Mon Sep 17 00:00:00 2001 From: jpark37 Date: Sun, 11 Jul 2021 04:38:16 -0700 Subject: [PATCH 6/7] libobs-opengl: Fix GS_R10G10B10A2 format --- libobs-opengl/gl-subsystem.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libobs-opengl/gl-subsystem.h b/libobs-opengl/gl-subsystem.h index c851243bd..da0d5819b 100644 --- a/libobs-opengl/gl-subsystem.h +++ b/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: From bf27941f5f04aa52750b34c9a204f4e7bab9bf5d Mon Sep 17 00:00:00 2001 From: jpark37 Date: Tue, 6 Jul 2021 22:10:57 -0700 Subject: [PATCH 7/7] libobs-winrt, win-capture: Linear SRGB support Update window, display, and game capture to always bilinear filter in linear space, even if the source texture is not SRGB typed. This helps resolve confusion in situations where we were filtering in nonlinear space vs. linear space, like when toggling an empty crop filter. --- libobs-winrt/winrt-capture.cpp | 24 +- libobs-winrt/winrt-capture.h | 3 +- plugins/win-capture/dc-capture.c | 51 +++-- plugins/win-capture/dc-capture.h | 4 +- .../win-capture/duplicator-monitor-capture.c | 91 ++++---- plugins/win-capture/game-capture.c | 207 +++++++++++++----- plugins/win-capture/monitor-capture.c | 2 +- plugins/win-capture/window-capture.c | 10 +- 8 files changed, 263 insertions(+), 129 deletions(-) diff --git a/libobs-winrt/winrt-capture.cpp b/libobs-winrt/winrt-capture.cpp index 5a0ce8fe2..d97b4b2aa 100644 --- a/libobs-winrt/winrt-capture.cpp +++ b/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 diff --git a/libobs-winrt/winrt-capture.h b/libobs-winrt/winrt-capture.h index 06252b204..9f2ccb4d5 100644 --- a/libobs-winrt/winrt-capture.h +++ b/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); diff --git a/plugins/win-capture/dc-capture.c b/plugins/win-capture/dc-capture.c index 7cb317225..a8839ba89 100644 --- a/plugins/win-capture/dc-capture.c +++ b/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); } diff --git a/plugins/win-capture/dc-capture.h b/plugins/win-capture/dc-capture.h index 8ff4bc3e2..15c08090f 100644 --- a/plugins/win-capture/dc-capture.h +++ b/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); diff --git a/plugins/win-capture/duplicator-monitor-capture.c b/plugins/win-capture/duplicator-monitor-capture.c index ad02b4b4c..fc9ee6587 100644 --- a/plugins/win-capture/duplicator-monitor-capture.c +++ b/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); + const bool previous = gs_framebuffer_srgb_enabled(); + gs_enable_framebuffer_srgb(true); + gs_enable_blending(false); - rot = capture->rot; + const int rot = capture->rot; + if (rot != 0) { + float x = 0.0f; + float y = 0.0f; - 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)); + 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); - - if (rot != 0) - gs_matrix_pop(); + gs_matrix_push(); + gs_matrix_translate3f(x, y, 0.0f); + gs_matrix_rotaa4f(0.0f, 0.0f, 1.0f, RAD((float)rot)); } - if (capture->capture_cursor) { - effect = obs_get_base_effect(OBS_EFFECT_DEFAULT); + 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); - while (gs_effect_loop(effect, "Draw")) { + 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) { + gs_effect_t *const default_effect = + obs_get_base_effect(OBS_EFFECT_DEFAULT); + + 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, diff --git a/plugins/win-capture/game-capture.c b/plugins/win-capture/game-capture.c index c18371aeb..abc2a9896 100644 --- a/plugins/win-capture/game-capture.c +++ b/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); + 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 (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"); + } obs_leave_graphics(); - if (!gc->texture) { - warn("init_shtex_capture: failed to open shared handle"); - return false; - } - - 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); - const bool linear_srgb = gs_get_linear_srgb() && gc->supports_srgb; - const bool previous = gs_set_linear_srgb(linear_srgb); + 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); - while (gs_effect_loop(effect, "Draw")) { - obs_source_draw(gc->texture, 0, 0, 0, 0, - gc->global_hook_info->flip); + gs_texrender_end(texrender); - if (gc->config.allow_transparency && gc->config.cursor && + texture = gs_texrender_get_texture(texrender); + } + } + + linear_sample = true; + } + + 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 (allow_transparency && gc->config.cursor && !gc->cursor_hidden) { game_capture_render_cursor(gc); } } - gs_set_linear_srgb(previous); + if (!allow_transparency && gc->config.cursor && !gc->cursor_hidden) { + gs_effect_t *const default_effect = + obs_get_base_effect(OBS_EFFECT_DEFAULT); - if (!gc->config.allow_transparency && gc->config.cursor && - !gc->cursor_hidden) { - 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); } } diff --git a/plugins/win-capture/monitor-capture.c b/plugins/win-capture/monitor-capture.c index 88e0f84c3..0e12345fe 100644 --- a/plugins/win-capture/monitor-capture.c +++ b/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); } diff --git a/plugins/win-capture/window-capture.c b/plugins/win-capture/window-capture.c index 72171f6b7..c9b1cf8b1 100644 --- a/plugins/win-capture/window-capture.c +++ b/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);