From ee2753961e582701b8c782aa5fd3eb8fa4d941ad Mon Sep 17 00:00:00 2001 From: jpark37 Date: Sun, 6 Mar 2022 22:36:45 -0800 Subject: [PATCH] obs-filters: Add HDR support to Render Delay --- plugins/obs-filters/gpu-delay.c | 110 ++++++++++++++++++++++++++++---- 1 file changed, 99 insertions(+), 11 deletions(-) diff --git a/plugins/obs-filters/gpu-delay.c b/plugins/obs-filters/gpu-delay.c index 4af8fdb9f..0eb1d6a54 100644 --- a/plugins/obs-filters/gpu-delay.c +++ b/plugins/obs-filters/gpu-delay.c @@ -7,6 +7,7 @@ struct frame { gs_texrender_t *render; + enum gs_color_space space; uint64_t ts; }; @@ -187,27 +188,73 @@ static void gpu_delay_filter_tick(void *data, float t) check_interval(f); } +static const char * +get_tech_name_and_multiplier(enum gs_color_space current_space, + enum gs_color_space source_space, + float *multiplier) +{ + const char *tech_name = "Draw"; + *multiplier = 1.f; + + switch (source_space) { + case GS_CS_SRGB: + case GS_CS_SRGB_16F: + switch (current_space) { + case GS_CS_709_SCRGB: + tech_name = "DrawMultiply"; + *multiplier = obs_get_video_sdr_white_level() / 80.0f; + } + break; + case GS_CS_709_EXTENDED: + switch (current_space) { + case GS_CS_SRGB: + case GS_CS_SRGB_16F: + tech_name = "DrawTonemap"; + break; + case GS_CS_709_SCRGB: + tech_name = "DrawMultiply"; + *multiplier = obs_get_video_sdr_white_level() / 80.0f; + } + break; + case GS_CS_709_SCRGB: + switch (current_space) { + case GS_CS_SRGB: + case GS_CS_SRGB_16F: + tech_name = "DrawMultiplyTonemap"; + *multiplier = 80.0f / obs_get_video_sdr_white_level(); + break; + case GS_CS_709_EXTENDED: + tech_name = "DrawMultiply"; + *multiplier = 80.0f / obs_get_video_sdr_white_level(); + } + } + + return tech_name; +} + static void draw_frame(struct gpu_delay_filter_data *f) { struct frame frame; circlebuf_peek_front(&f->frames, &frame, sizeof(frame)); + const enum gs_color_space current_space = gs_get_color_space(); + float multiplier; + const char *technique = get_tech_name_and_multiplier( + current_space, frame.space, &multiplier); + gs_effect_t *effect = obs_get_base_effect(OBS_EFFECT_DEFAULT); gs_texture_t *tex = gs_texrender_get_texture(frame.render); if (tex) { - 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_eparam_t *image = - gs_effect_get_param_by_name(effect, "image"); - if (linear_srgb) - gs_effect_set_texture_srgb(image, tex); - else - gs_effect_set_texture(image, tex); + gs_effect_set_texture_srgb( + gs_effect_get_param_by_name(effect, "image"), tex); + gs_effect_set_float(gs_effect_get_param_by_name(effect, + "multiplier"), + multiplier); - while (gs_effect_loop(effect, "Draw")) + while (gs_effect_loop(effect, technique)) gs_draw_sprite(tex, 0, f->cx, f->cy); gs_enable_framebuffer_srgb(previous); @@ -233,12 +280,26 @@ static void gpu_delay_filter_render(void *data, gs_effect_t *effect) struct frame frame; circlebuf_pop_front(&f->frames, &frame, sizeof(frame)); + const enum gs_color_space preferred_spaces[] = { + GS_CS_SRGB, + GS_CS_SRGB_16F, + GS_CS_709_EXTENDED, + }; + const enum gs_color_space space = obs_source_get_color_space( + parent, OBS_COUNTOF(preferred_spaces), preferred_spaces); + const enum gs_color_format format = gs_get_format_from_space(space); + if (gs_texrender_get_format(frame.render) != format) { + gs_texrender_destroy(frame.render); + frame.render = gs_texrender_create(format, GS_ZS_NONE); + } + gs_texrender_reset(frame.render); gs_blend_state_push(); gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO); - if (gs_texrender_begin(frame.render, f->cx, f->cy)) { + if (gs_texrender_begin_with_color_space(frame.render, f->cx, f->cy, + space)) { uint32_t parent_flags = obs_source_get_output_flags(target); bool custom_draw = (parent_flags & OBS_SOURCE_CUSTOM_DRAW) != 0; bool async = (parent_flags & OBS_SOURCE_ASYNC) != 0; @@ -255,6 +316,8 @@ static void gpu_delay_filter_render(void *data, gs_effect_t *effect) obs_source_video_render(target); gs_texrender_end(frame.render); + + frame.space = space; } gs_blend_state_pop(); @@ -266,6 +329,30 @@ static void gpu_delay_filter_render(void *data, gs_effect_t *effect) UNUSED_PARAMETER(effect); } +static enum gs_color_space +gpu_delay_filter_get_color_space(void *data, size_t count, + const enum gs_color_space *preferred_spaces) +{ + struct gpu_delay_filter_data *const f = data; + obs_source_t *target = obs_filter_get_target(f->context); + obs_source_t *parent = obs_filter_get_parent(f->context); + + if (!f->target_valid || !target || !parent || !f->frames.size) { + return (count > 0) ? preferred_spaces[0] : GS_CS_SRGB; + } + + struct frame frame; + circlebuf_peek_front(&f->frames, &frame, sizeof(frame)); + enum gs_color_space space = frame.space; + for (size_t i = 0; i < count; ++i) { + space = preferred_spaces[i]; + if (space == frame.space) + break; + } + + return space; +} + struct obs_source_info gpu_delay_filter = { .id = "gpu_delay", .type = OBS_SOURCE_TYPE_FILTER, @@ -277,4 +364,5 @@ struct obs_source_info gpu_delay_filter = { .get_properties = gpu_delay_filter_properties, .video_tick = gpu_delay_filter_tick, .video_render = gpu_delay_filter_render, + .video_get_color_space = gpu_delay_filter_get_color_space, };