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-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) 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 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: 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/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 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) { 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, 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);