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);