From 06111d5b10ce7872c537bc4e78ac690ccc596167 Mon Sep 17 00:00:00 2001 From: jpark37 Date: Wed, 30 Mar 2022 22:39:53 -0700 Subject: [PATCH] libobs: Add high-precision sRGB support --- .../reference-libobs-graphics-graphics.rst | 1 + libobs/data/default.effect | 19 +++ libobs/data/format_conversion.effect | 150 +++++++++++++++++- libobs/graphics/graphics.h | 2 + libobs/obs-internal.h | 10 +- libobs/obs-scene.c | 8 + libobs/obs-source-deinterlace.c | 13 +- libobs/obs-source-transition.c | 33 ++-- libobs/obs-source.c | 144 ++++++++++------- libobs/obs.c | 50 ++++-- 10 files changed, 329 insertions(+), 101 deletions(-) diff --git a/docs/sphinx/reference-libobs-graphics-graphics.rst b/docs/sphinx/reference-libobs-graphics-graphics.rst index b1245acca..d39bcff59 100644 --- a/docs/sphinx/reference-libobs-graphics-graphics.rst +++ b/docs/sphinx/reference-libobs-graphics-graphics.rst @@ -52,6 +52,7 @@ Graphics Enumerations Color space. Can be one of the following values: - GS_CS_SRGB - sRGB + - GS_CS_SRGB_16F - High-precision SDR - GS_CS_709_EXTENDED - Canvas, Mac EDR (HDR) - GS_CS_709_SCRGB - 1.0 = 80 nits, Windows/Linux HDR diff --git a/libobs/data/default.effect b/libobs/data/default.effect index 86612628b..f329565d2 100644 --- a/libobs/data/default.effect +++ b/libobs/data/default.effect @@ -45,6 +45,16 @@ float4 PSDrawNonlinearAlpha(VertInOut vert_in) : TARGET return rgba; } +float4 PSDrawNonlinearAlphaMultiply(VertInOut vert_in) : TARGET +{ + float4 rgba = image.Sample(def_sampler, vert_in.uv); + rgba.rgb = srgb_linear_to_nonlinear(rgba.rgb); + rgba.rgb *= rgba.a; + rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb); + rgba.rgb *= multiplier; + return rgba; +} + float4 PSDrawSrgbDecompress(VertInOut vert_in) : TARGET { float4 rgba = image.Sample(def_sampler, vert_in.uv); @@ -105,6 +115,15 @@ technique DrawNonlinearAlpha } } +technique DrawNonlinearAlphaMultiply +{ + pass + { + vertex_shader = VSDefault(vert_in); + pixel_shader = PSDrawNonlinearAlphaMultiply(vert_in); + } +} + technique DrawSrgbDecompress { pass diff --git a/libobs/data/format_conversion.effect b/libobs/data/format_conversion.effect index a63286f3f..84dd478b5 100644 --- a/libobs/data/format_conversion.effect +++ b/libobs/data/format_conversion.effect @@ -205,7 +205,7 @@ float PS_PQ_Y_709_2020(FragPos frag_in) : TARGET rgb = rec709_to_rec2020(rgb); rgb = linear_to_st2084(rgb); float y = dot(color_vec0.xyz, rgb) + color_vec0.w; - y = (65472. / 65535.) * y + 0.00048828125; // set up truncation to 10 bits + y = (65472. / 65535.) * y + (32. / 65535.); // set up truncation to 10 bits return y; } @@ -215,7 +215,16 @@ float PS_HLG_Y_709_2020(FragPos frag_in) : TARGET rgb = rec709_to_rec2020(rgb); rgb = linear_to_hlg(rgb); float y = dot(color_vec0.xyz, rgb) + color_vec0.w; - y = (65472. / 65535.) * y + 0.00048828125; // set up truncation to 10 bits + y = (65472. / 65535.) * y + (32. / 65535.); // set up truncation to 10 bits + return y; +} + +float PS_SRGB_Y(FragPos frag_in) : TARGET +{ + float3 rgb = image.Load(int3(frag_in.pos.xy, 0)).rgb; + rgb = srgb_linear_to_nonlinear(rgb); + float y = dot(color_vec0.xyz, rgb) + color_vec0.w; + y = (65472. / 65535.) * y + (32. / 65535.); // set up truncation to 10 bits return y; } @@ -237,6 +246,14 @@ float PS_I010_HLG_Y_709_2020(FragPos frag_in) : TARGET return y * (1023. / 65535.); } +float PS_I010_SRGB_Y(FragPos frag_in) : TARGET +{ + float3 rgb = image.Load(int3(frag_in.pos.xy, 0)).rgb; + rgb = srgb_linear_to_nonlinear(rgb); + float y = dot(color_vec0.xyz, rgb) + color_vec0.w; + return y * (1023. / 65535.); +} + float2 PS_UV_Wide(FragTexWide frag_in) : TARGET { float3 rgb_left = image.Sample(def_sampler, frag_in.uuv.xz).rgb; @@ -259,7 +276,7 @@ float2 PS_PQ_UV_709_2020_WideWide(FragTexWideWide frag_in) : TARGET float u = dot(color_vec1.xyz, rgb) + color_vec1.w; float v = dot(color_vec2.xyz, rgb) + color_vec2.w; float2 uv = float2(u, v); - uv = (65472. / 65535.) * uv + 0.00048828125; // set up truncation to 10 bits + uv = (65472. / 65535.) * uv + (32. / 65535.); // set up truncation to 10 bits return uv; } @@ -275,7 +292,22 @@ float2 PS_HLG_UV_709_2020_WideWide(FragTexWideWide frag_in) : TARGET float u = dot(color_vec1.xyz, rgb) + color_vec1.w; float v = dot(color_vec2.xyz, rgb) + color_vec2.w; float2 uv = float2(u, v); - uv = (65472. / 65535.) * uv + 0.00048828125; // set up truncation to 10 bits + uv = (65472. / 65535.) * uv + (32. / 65535.); // set up truncation to 10 bits + return uv; +} + +float2 PS_SRGB_UV_WideWide(FragTexWideWide frag_in) : TARGET +{ + float3 rgb_topleft = image.Sample(def_sampler, frag_in.uuvv.xz).rgb; + float3 rgb_topright = image.Sample(def_sampler, frag_in.uuvv.yz).rgb; + float3 rgb_bottomleft = image.Sample(def_sampler, frag_in.uuvv.xw).rgb; + float3 rgb_bottomright = image.Sample(def_sampler, frag_in.uuvv.yw).rgb; + float3 rgb = (rgb_topleft + rgb_topright + rgb_bottomleft + rgb_bottomright) * 0.25; + rgb = srgb_linear_to_nonlinear(rgb); + float u = dot(color_vec1.xyz, rgb) + color_vec1.w; + float v = dot(color_vec2.xyz, rgb) + color_vec2.w; + float2 uv = float2(u, v); + uv = (65472. / 65535.) * uv + (32. / 65535.); // set up truncation to 10 bits return uv; } @@ -337,6 +369,18 @@ float PS_I010_HLG_U_709_2020_WideWide(FragTexWideWide frag_in) : TARGET return u * (1023. / 65535.); } +float PS_I010_SRGB_U_WideWide(FragTexWideWide frag_in) : TARGET +{ + float3 rgb_topleft = image.Sample(def_sampler, frag_in.uuvv.xz).rgb; + float3 rgb_topright = image.Sample(def_sampler, frag_in.uuvv.yz).rgb; + float3 rgb_bottomleft = image.Sample(def_sampler, frag_in.uuvv.xw).rgb; + float3 rgb_bottomright = image.Sample(def_sampler, frag_in.uuvv.yw).rgb; + float3 rgb = (rgb_topleft + rgb_topright + rgb_bottomleft + rgb_bottomright) * 0.25; + rgb = srgb_linear_to_nonlinear(rgb); + float u = dot(color_vec1.xyz, rgb) + color_vec1.w; + return u * (1023. / 65535.); +} + float PS_I010_PQ_V_709_2020_WideWide(FragTexWideWide frag_in) : TARGET { float3 rgb_topleft = image.Sample(def_sampler, frag_in.uuvv.xz).rgb; @@ -363,6 +407,18 @@ float PS_I010_HLG_V_709_2020_WideWide(FragTexWideWide frag_in) : TARGET return v * (1023. / 65535.); } +float PS_I010_SRGB_V_WideWide(FragTexWideWide frag_in) : TARGET +{ + float3 rgb_topleft = image.Sample(def_sampler, frag_in.uuvv.xz).rgb; + float3 rgb_topright = image.Sample(def_sampler, frag_in.uuvv.yz).rgb; + float3 rgb_bottomleft = image.Sample(def_sampler, frag_in.uuvv.xw).rgb; + float3 rgb_bottomright = image.Sample(def_sampler, frag_in.uuvv.yw).rgb; + float3 rgb = (rgb_topleft + rgb_topright + rgb_bottomleft + rgb_bottomright) * 0.25; + rgb = srgb_linear_to_nonlinear(rgb); + float v = dot(color_vec2.xyz, rgb) + color_vec2.w; + return v * (1023. / 65535.); +} + float3 YUV_to_RGB(float3 yuv) { yuv = clamp(yuv, color_range_min, color_range_max); @@ -495,6 +551,19 @@ float3 PSNV12_Reverse(VertTexPos frag_in) : TARGET return rgb; } +float4 PSI010_SRGB_Reverse(VertTexPos frag_in) : TARGET +{ + float ratio = 65535. / 1023.; + float y = image.Load(int3(frag_in.pos.xy, 0)).x * ratio; + int3 xy0_chroma = int3(frag_in.uv, 0); + float cb = image1.Load(xy0_chroma).x * ratio; + float cr = image2.Load(xy0_chroma).x * ratio; + float3 yuv = float3(y, cb, cr); + float3 rgb = YUV_to_RGB(yuv); + rgb = srgb_nonlinear_to_linear(rgb); + return float4(rgb, 1.0); +} + float4 PSI010_PQ_2020_709_Reverse(VertTexPos frag_in) : TARGET { float ratio = 65535. / 1023.; @@ -523,6 +592,16 @@ float4 PSI010_HLG_2020_709_Reverse(VertTexPos frag_in) : TARGET return float4(rgb, 1.0); } +float4 PSP010_SRGB_Reverse(VertTexPos frag_in) : TARGET +{ + float y = image.Load(int3(frag_in.pos.xy, 0)).x; + float2 cbcr = image1.Load(int3(frag_in.uv, 0)).xy; + float3 yuv = float3(y, cbcr); + float3 rgb = YUV_to_RGB(yuv); + rgb = srgb_nonlinear_to_linear(rgb); + return float4(rgb, 1.0); +} + float4 PSP010_PQ_2020_709_Reverse(VertTexPos frag_in) : TARGET { float y = image.Load(int3(frag_in.pos.xy, 0)).x; @@ -669,6 +748,15 @@ technique I010_HLG_Y } } +technique I010_SRGB_Y +{ + pass + { + vertex_shader = VSPos(id); + pixel_shader = PS_I010_SRGB_Y(frag_in); + } +} + technique I010_PQ_U { pass @@ -687,6 +775,15 @@ technique I010_HLG_U } } +technique I010_SRGB_U +{ + pass + { + vertex_shader = VSTexPos_TopLeft(id); + pixel_shader = PS_I010_SRGB_U_WideWide(frag_in); + } +} + technique I010_PQ_V { pass @@ -705,6 +802,15 @@ technique I010_HLG_V } } +technique I010_SRGB_V +{ + pass + { + vertex_shader = VSTexPos_TopLeft(id); + pixel_shader = PS_I010_SRGB_V_WideWide(frag_in); + } +} + technique P010_PQ_Y { pass @@ -723,6 +829,15 @@ technique P010_HLG_Y } } +technique P010_SRGB_Y +{ + pass + { + vertex_shader = VSPos(id); + pixel_shader = PS_SRGB_Y(frag_in); + } +} + technique P010_PQ_UV { pass @@ -741,6 +856,15 @@ technique P010_HLG_UV } } +technique P010_SRGB_UV +{ + pass + { + vertex_shader = VSTexPos_TopLeft(id); + pixel_shader = PS_SRGB_UV_WideWide(frag_in); + } +} + technique UYVY_Reverse { pass @@ -840,6 +964,15 @@ technique NV12_Reverse } } +technique I010_SRGB_Reverse +{ + pass + { + vertex_shader = VSTexPosHalfHalf_Reverse(id); + pixel_shader = PSI010_SRGB_Reverse(frag_in); + } +} + technique I010_PQ_2020_709_Reverse { pass @@ -858,6 +991,15 @@ technique I010_HLG_2020_709_Reverse } } +technique P010_SRGB_Reverse +{ + pass + { + vertex_shader = VSTexPosHalfHalf_Reverse(id); + pixel_shader = PSP010_SRGB_Reverse(frag_in); + } +} + technique P010_PQ_2020_709_Reverse { pass diff --git a/libobs/graphics/graphics.h b/libobs/graphics/graphics.h index aa11dc874..696a932f9 100644 --- a/libobs/graphics/graphics.h +++ b/libobs/graphics/graphics.h @@ -81,6 +81,7 @@ enum gs_color_format { enum gs_color_space { GS_CS_SRGB, /* SDR */ + GS_CS_SRGB_16F, /* High-precision SDR */ GS_CS_709_EXTENDED, /* Canvas, Mac EDR (HDR) */ GS_CS_709_SCRGB, /* 1.0 = 80 nits, Windows/Linux HDR */ }; @@ -1061,6 +1062,7 @@ gs_get_format_from_space(enum gs_color_space space) switch (space) { case GS_CS_SRGB: break; + case GS_CS_SRGB_16F: case GS_CS_709_EXTENDED: case GS_CS_709_SCRGB: return GS_RGBA16F; diff --git a/libobs/obs-internal.h b/libobs/obs-internal.h index 2551d72c6..8a600c30c 100644 --- a/libobs/obs-internal.h +++ b/libobs/obs-internal.h @@ -898,14 +898,14 @@ convert_video_format(enum video_format format) } static inline enum gs_color_space -convert_video_space(enum video_format format, size_t count, +convert_video_space(enum video_format format, enum video_trc trc, + enum gs_color_format color_format, size_t count, const enum gs_color_space *preferred_spaces) { enum gs_color_space video_space = GS_CS_SRGB; - switch (format) { - case VIDEO_FORMAT_I010: - case VIDEO_FORMAT_P010: - video_space = GS_CS_709_EXTENDED; + if (color_format == GS_RGBA16F) { + video_space = (trc == VIDEO_TRC_SRGB) ? GS_CS_SRGB_16F + : GS_CS_709_EXTENDED; } enum gs_color_space space = video_space; diff --git a/libobs/obs-scene.c b/libobs/obs-scene.c index 1f969aea7..aaf8b2f11 100644 --- a/libobs/obs-scene.c +++ b/libobs/obs-scene.c @@ -612,6 +612,7 @@ static void render_item_texture(struct obs_scene_item *item, case GS_CS_709_SCRGB: switch (source_space) { case GS_CS_SRGB: + case GS_CS_SRGB_16F: case GS_CS_709_EXTENDED: multiplier = obs_get_video_sdr_white_level() / 80.f; } @@ -621,6 +622,7 @@ static void render_item_texture(struct obs_scene_item *item, case GS_CS_709_SCRGB: switch (current_space) { case GS_CS_SRGB: + case GS_CS_SRGB_16F: case GS_CS_709_EXTENDED: multiplier = 80.f / obs_get_video_sdr_white_level(); } @@ -631,6 +633,7 @@ static void render_item_texture(struct obs_scene_item *item, tech_name = "DrawUpscale"; switch (source_space) { case GS_CS_SRGB: + case GS_CS_SRGB_16F: switch (current_space) { case GS_CS_709_SCRGB: tech_name = "DrawUpscaleMultiply"; @@ -639,6 +642,7 @@ static void render_item_texture(struct obs_scene_item *item, case GS_CS_709_EXTENDED: switch (current_space) { case GS_CS_SRGB: + case GS_CS_SRGB_16F: tech_name = "DrawUpscaleTonemap"; break; case GS_CS_709_SCRGB: @@ -648,6 +652,7 @@ static void render_item_texture(struct obs_scene_item *item, case GS_CS_709_SCRGB: switch (current_space) { case GS_CS_SRGB: + case GS_CS_SRGB_16F: tech_name = "DrawUpscaleMultiplyTonemap"; break; case GS_CS_709_EXTENDED: @@ -657,6 +662,7 @@ static void render_item_texture(struct obs_scene_item *item, } else { switch (source_space) { case GS_CS_SRGB: + case GS_CS_SRGB_16F: switch (current_space) { case GS_CS_709_SCRGB: tech_name = "DrawMultiply"; @@ -665,6 +671,7 @@ static void render_item_texture(struct obs_scene_item *item, 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: @@ -674,6 +681,7 @@ static void render_item_texture(struct obs_scene_item *item, case GS_CS_709_SCRGB: switch (current_space) { case GS_CS_SRGB: + case GS_CS_SRGB_16F: tech_name = "DrawMultiplyTonemap"; break; case GS_CS_709_EXTENDED: diff --git a/libobs/obs-source-deinterlace.c b/libobs/obs-source-deinterlace.c index 04d3d3552..11203c535 100644 --- a/libobs/obs-source-deinterlace.c +++ b/libobs/obs-source-deinterlace.c @@ -393,9 +393,13 @@ void deinterlace_render(obs_source_t *s) if (!cur_tex || !prev_tex || !s->async_width || !s->async_height) return; - const enum gs_color_space source_space = - (s->async_color_format == GS_RGBA16F) ? GS_CS_709_EXTENDED - : GS_CS_SRGB; + enum gs_color_space source_space = GS_CS_SRGB; + if (s->async_color_format == GS_RGBA16F) { + source_space = (s->async_trc == VIDEO_TRC_SRGB) + ? GS_CS_SRGB_16F + : GS_CS_709_EXTENDED; + } + const bool linear_srgb = (source_space != GS_CS_SRGB) || gs_get_linear_srgb() || deinterlace_linear_required(s->deinterlace_mode); @@ -405,6 +409,7 @@ void deinterlace_render(obs_source_t *s) float multiplier = 1.0; switch (source_space) { case GS_CS_SRGB: + case GS_CS_SRGB_16F: switch (current_space) { case GS_CS_709_SCRGB: tech_name = "DrawMultiply"; @@ -414,6 +419,7 @@ void deinterlace_render(obs_source_t *s) 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: @@ -424,6 +430,7 @@ void deinterlace_render(obs_source_t *s) 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; diff --git a/libobs/obs-source-transition.c b/libobs/obs-source-transition.c index 5c312b14e..17e696788 100644 --- a/libobs/obs-source-transition.c +++ b/libobs/obs-source-transition.c @@ -832,12 +832,16 @@ void obs_transition_video_render2( static enum gs_color_space mix_spaces(enum gs_color_space a, enum gs_color_space b) { - assert((a == GS_CS_SRGB) || (a == GS_CS_709_EXTENDED)); - assert((b == GS_CS_SRGB) || (b == GS_CS_709_EXTENDED)); + assert((a == GS_CS_SRGB) || (a == GS_CS_SRGB_16F) || + (a == GS_CS_709_EXTENDED)); + assert((b == GS_CS_SRGB) || (b == GS_CS_SRGB_16F) || + (b == GS_CS_709_EXTENDED)); - return ((a == GS_CS_709_EXTENDED) || (b == GS_CS_709_EXTENDED)) - ? GS_CS_709_EXTENDED - : GS_CS_SRGB; + if ((a == GS_CS_709_EXTENDED) || (b == GS_CS_709_EXTENDED)) + return GS_CS_709_EXTENDED; + if ((a == GS_CS_SRGB_16F) || (b == GS_CS_SRGB_16F)) + return GS_CS_SRGB_16F; + return GS_CS_SRGB; } enum gs_color_space @@ -846,25 +850,26 @@ obs_transition_video_get_color_space(obs_source_t *transition) obs_source_t *source0 = transition->transition_sources[0]; obs_source_t *source1 = transition->transition_sources[1]; - const enum gs_color_space dual_spaces[] = { + const enum gs_color_space preferred_spaces[] = { GS_CS_SRGB, + GS_CS_SRGB_16F, GS_CS_709_EXTENDED, }; enum gs_color_space space = GS_CS_SRGB; if (source0) { - space = mix_spaces(space, - obs_source_get_color_space( - source0, OBS_COUNTOF(dual_spaces), - dual_spaces)); + space = mix_spaces(space, obs_source_get_color_space( + source0, + OBS_COUNTOF(preferred_spaces), + preferred_spaces)); } if (source1) { - space = mix_spaces(space, - obs_source_get_color_space( - source1, OBS_COUNTOF(dual_spaces), - dual_spaces)); + space = mix_spaces(space, obs_source_get_color_space( + source1, + OBS_COUNTOF(preferred_spaces), + preferred_spaces)); } return space; diff --git a/libobs/obs-source.c b/libobs/obs-source.c index 4e8bb2529..24f7f2474 100644 --- a/libobs/obs-source.c +++ b/libobs/obs-source.c @@ -1550,8 +1550,10 @@ enum convert_type { CONVERT_800, CONVERT_RGB_LIMITED, CONVERT_BGR3, + CONVERT_I010_SRGB, CONVERT_I010_PQ_2020_709, CONVERT_I010_HLG_2020_709, + CONVERT_P010_SRGB, CONVERT_P010_PQ_2020_709, CONVERT_P010_HLG_2020_709, }; @@ -1599,15 +1601,25 @@ static inline enum convert_type get_convert_type(enum video_format format, return CONVERT_444_A_PACK; case VIDEO_FORMAT_I010: { - const bool hlg = trc == VIDEO_TRC_HLG; - return hlg ? CONVERT_I010_HLG_2020_709 - : CONVERT_I010_PQ_2020_709; + switch (trc) { + case VIDEO_TRC_SRGB: + return CONVERT_I010_SRGB; + case VIDEO_TRC_HLG: + return CONVERT_I010_HLG_2020_709; + default: + return CONVERT_I010_PQ_2020_709; + } } case VIDEO_FORMAT_P010: { - const bool hlg = trc == VIDEO_TRC_HLG; - return hlg ? CONVERT_P010_HLG_2020_709 - : CONVERT_P010_PQ_2020_709; + switch (trc) { + case VIDEO_TRC_SRGB: + return CONVERT_P010_SRGB; + case VIDEO_TRC_HLG: + return CONVERT_P010_HLG_2020_709; + default: + return CONVERT_P010_PQ_2020_709; + } } } @@ -1885,10 +1897,12 @@ static inline bool init_gpu_conversion(struct obs_source *source, case CONVERT_444_A_PACK: return set_packed444_alpha_sizes(source, frame); + case CONVERT_I010_SRGB: case CONVERT_I010_PQ_2020_709: case CONVERT_I010_HLG_2020_709: return set_i010_sizes(source, frame); + case CONVERT_P010_SRGB: case CONVERT_P010_PQ_2020_709: case CONVERT_P010_HLG_2020_709: return set_p010_sizes(source, frame); @@ -1980,8 +1994,10 @@ static void upload_raw_frame(gs_texture_t *tex[MAX_AV_PLANES], case CONVERT_422_A: case CONVERT_444_A: case CONVERT_444_A_PACK: + case CONVERT_I010_SRGB: case CONVERT_I010_PQ_2020_709: case CONVERT_I010_HLG_2020_709: + case CONVERT_P010_SRGB: case CONVERT_P010_PQ_2020_709: case CONVERT_P010_HLG_2020_709: for (size_t c = 0; c < MAX_AV_PLANES; c++) { @@ -2041,15 +2057,25 @@ static const char *select_conversion_technique(enum video_format format, return "AYUV_Reverse"; case VIDEO_FORMAT_I010: { - const bool hlg = trc == VIDEO_TRC_HLG; - return hlg ? "I010_HLG_2020_709_Reverse" - : "I010_PQ_2020_709_Reverse"; + switch (trc) { + case VIDEO_TRC_SRGB: + return "I010_SRGB_Reverse"; + case VIDEO_TRC_HLG: + return "I010_HLG_2020_709_Reverse"; + default: + return "I010_PQ_2020_709_Reverse"; + } } case VIDEO_FORMAT_P010: { - const bool hlg = trc == VIDEO_TRC_HLG; - return hlg ? "P010_HLG_2020_709_Reverse" - : "P010_PQ_2020_709_Reverse"; + switch (trc) { + case VIDEO_TRC_SRGB: + return "P010_SRGB_Reverse"; + case VIDEO_TRC_HLG: + return "P010_HLG_2020_709_Reverse"; + default: + return "P010_PQ_2020_709_Reverse"; + } } case VIDEO_FORMAT_BGRA: @@ -2240,42 +2266,6 @@ static inline void obs_source_draw_texture(struct obs_source *source, gs_enable_framebuffer_srgb(previous); } -static void obs_source_draw_async_texture(struct obs_source *source) -{ - gs_effect_t *effect = gs_get_effect(); - bool def_draw = (!effect); - bool premultiplied = false; - gs_technique_t *tech = NULL; - - if (def_draw) { - effect = obs_get_base_effect(OBS_EFFECT_DEFAULT); - const bool nonlinear_alpha = gs_get_linear_srgb() && - !source->async_linear_alpha; - const char *tech_name = nonlinear_alpha ? "DrawNonlinearAlpha" - : "Draw"; - premultiplied = nonlinear_alpha; - tech = gs_effect_get_technique(effect, tech_name); - gs_technique_begin(tech); - gs_technique_begin_pass(tech, 0); - } - - if (premultiplied) { - gs_blend_state_push(); - gs_blend_function(GS_BLEND_ONE, GS_BLEND_INVSRCALPHA); - } - - obs_source_draw_texture(source, effect); - - if (premultiplied) { - gs_blend_state_pop(); - } - - if (def_draw) { - gs_technique_end_pass(tech); - gs_technique_end(tech); - } -} - static void recreate_async_texture(obs_source_t *source, enum gs_color_format format) { @@ -2354,37 +2344,53 @@ static void rotate_async_video(obs_source_t *source, long rotation) static inline void obs_source_render_async_video(obs_source_t *source) { if (source->async_textures[0] && source->async_active) { + enum gs_color_space source_space = GS_CS_SRGB; + if (source->async_color_format == GS_RGBA16F) { + source_space = (source->async_trc == VIDEO_TRC_SRGB) + ? GS_CS_SRGB_16F + : GS_CS_709_EXTENDED; + } + gs_effect_t *const effect = obs_get_base_effect(OBS_EFFECT_DEFAULT); const char *tech_name = "Draw"; float multiplier = 1.0; - const enum gs_color_space source_space = - (source->async_color_format == GS_RGBA16F) - ? GS_CS_709_EXTENDED - : GS_CS_SRGB; const enum gs_color_space current_space = gs_get_color_space(); - bool linear_srgb = gs_get_linear_srgb(); + const bool linear_srgb = gs_get_linear_srgb(); + bool nonlinear_alpha = false; switch (source_space) { case GS_CS_SRGB: + nonlinear_alpha = linear_srgb && + !source->async_linear_alpha; switch (current_space) { case GS_CS_SRGB: - if (linear_srgb && - !source->async_linear_alpha) { + case GS_CS_SRGB_16F: + case GS_CS_709_EXTENDED: + if (nonlinear_alpha) tech_name = "DrawNonlinearAlpha"; - } break; + case GS_CS_709_SCRGB: + tech_name = + nonlinear_alpha + ? "DrawNonlinearAlphaMultiply" + : "DrawMultiply"; + multiplier = + obs_get_video_sdr_white_level() / 80.0f; + } + break; + 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; - linear_srgb = true; } break; case GS_CS_709_EXTENDED: switch (current_space) { case GS_CS_SRGB: + case GS_CS_SRGB_16F: tech_name = "DrawTonemap"; - linear_srgb = true; break; case GS_CS_709_SCRGB: tech_name = "DrawMultiply"; @@ -2395,10 +2401,10 @@ static inline void obs_source_render_async_video(obs_source_t *source) 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(); - linear_srgb = true; break; case GS_CS_709_EXTENDED: tech_name = "DrawMultiply"; @@ -2422,7 +2428,18 @@ static inline void obs_source_render_async_video(obs_source_t *source) gs_matrix_push(); rotate_async_video(source, rotation); } - obs_source_draw_async_texture(source); + + if (nonlinear_alpha) { + gs_blend_state_push(); + gs_blend_function(GS_BLEND_ONE, GS_BLEND_INVSRCALPHA); + } + + obs_source_draw_texture(source, effect); + + if (nonlinear_alpha) { + gs_blend_state_pop(); + } + if (rotation) { gs_matrix_pop(); } @@ -2509,6 +2526,7 @@ static void source_render(obs_source_t *source, gs_effect_t *effect) enum gs_color_format format = gs_get_format_from_space(source_space); switch (source_space) { case GS_CS_SRGB: + case GS_CS_SRGB_16F: switch (current_space) { case GS_CS_709_EXTENDED: convert_tech = "Draw"; @@ -2521,6 +2539,7 @@ static void source_render(obs_source_t *source, gs_effect_t *effect) case GS_CS_709_EXTENDED: switch (current_space) { case GS_CS_SRGB: + case GS_CS_SRGB_16F: convert_tech = "DrawTonemap"; break; case GS_CS_709_SCRGB: @@ -2531,6 +2550,7 @@ static void source_render(obs_source_t *source, gs_effect_t *effect) case GS_CS_709_SCRGB: switch (current_space) { case GS_CS_SRGB: + case GS_CS_SRGB_16F: convert_tech = "DrawMultiplyTonemap"; multiplier = 80.0f / obs_get_video_sdr_white_level(); break; @@ -2796,7 +2816,9 @@ obs_source_get_color_space(obs_source_t *source, size_t count, } if (source->info.output_flags & OBS_SOURCE_ASYNC) { - return convert_video_space(source->async_format, count, + return convert_video_space(source->async_format, + source->async_trc, + source->async_color_format, count, preferred_spaces); } diff --git a/libobs/obs.c b/libobs/obs.c index 72e406e78..3c21038ab 100644 --- a/libobs/obs.c +++ b/libobs/obs.c @@ -80,28 +80,35 @@ static inline void calc_gpu_conversion_sizes(const struct obs_video_info *ovi) video->conversion_needed = true; video->conversion_width_i = 1.f / (float)ovi->output_width; video->conversion_height_i = 1.f / (float)ovi->output_height; - if (ovi->colorspace == VIDEO_CS_2020_HLG) { + if (ovi->colorspace == VIDEO_CS_2020_PQ) { + video->conversion_techs[0] = "I010_PQ_Y"; + video->conversion_techs[1] = "I010_PQ_U"; + video->conversion_techs[2] = "I010_PQ_V"; + } else if (ovi->colorspace == VIDEO_CS_2020_HLG) { video->conversion_techs[0] = "I010_HLG_Y"; video->conversion_techs[1] = "I010_HLG_U"; video->conversion_techs[2] = "I010_HLG_V"; video->maximum_nits = 1000.f; } else { - video->conversion_techs[0] = "I010_PQ_Y"; - video->conversion_techs[1] = "I010_PQ_U"; - video->conversion_techs[2] = "I010_PQ_V"; + video->conversion_techs[0] = "I010_SRGB_Y"; + video->conversion_techs[1] = "I010_SRGB_U"; + video->conversion_techs[2] = "I010_SRGB_V"; } break; case VIDEO_FORMAT_P010: video->conversion_needed = true; video->conversion_width_i = 1.f / (float)ovi->output_width; video->conversion_height_i = 1.f / (float)ovi->output_height; - if (ovi->colorspace == VIDEO_CS_2020_HLG) { + if (ovi->colorspace == VIDEO_CS_2020_PQ) { + video->conversion_techs[0] = "P010_PQ_Y"; + video->conversion_techs[1] = "P010_PQ_UV"; + } else if (ovi->colorspace == VIDEO_CS_2020_HLG) { video->conversion_techs[0] = "P010_HLG_Y"; video->conversion_techs[1] = "P010_HLG_UV"; video->maximum_nits = 1000.f; } else { - video->conversion_techs[0] = "P010_PQ_Y"; - video->conversion_techs[1] = "P010_PQ_UV"; + video->conversion_techs[0] = "P010_SRGB_Y"; + video->conversion_techs[1] = "P010_SRGB_UV"; } } } @@ -372,12 +379,24 @@ static bool obs_init_textures(struct obs_video_info *ovi) } enum gs_color_format format = GS_RGBA; + switch (ovi->output_format) { + case VIDEO_FORMAT_I010: + case VIDEO_FORMAT_P010: + format = GS_RGBA16F; + } + enum gs_color_space space = GS_CS_SRGB; switch (ovi->colorspace) { case VIDEO_CS_2020_PQ: case VIDEO_CS_2020_HLG: - format = GS_RGBA16F; space = GS_CS_709_EXTENDED; + break; + default: + switch (ovi->output_format) { + case VIDEO_FORMAT_I010: + case VIDEO_FORMAT_P010: + space = GS_CS_SRGB_16F; + } } video->render_texture = gs_texture_create(ovi->base_width, @@ -1927,13 +1946,16 @@ static void obs_render_main_texture_internal(enum gs_blend_type src_c, const enum gs_color_space source_space = video->render_space; const enum gs_color_space current_space = gs_get_color_space(); const char *tech_name = "Draw"; - float multiplier = 1.0f; - if ((current_space == GS_CS_SRGB) && - (source_space == GS_CS_709_EXTENDED)) { - tech_name = "DrawTonemap"; - } else if (current_space == GS_CS_709_SCRGB) { + float multiplier = 1.f; + switch (current_space) { + case GS_CS_SRGB: + case GS_CS_SRGB_16F: + if (source_space == GS_CS_709_EXTENDED) + tech_name = "DrawTonemap"; + break; + case GS_CS_709_SCRGB: tech_name = "DrawMultiply"; - multiplier = obs_get_video_sdr_white_level() / 80.0f; + multiplier = obs_get_video_sdr_white_level() / 80.f; } const bool previous = gs_framebuffer_srgb_enabled();