libobs: Add high-precision sRGB support

master
jpark37 2022-03-30 22:39:53 -07:00 committed by Jim
parent 848e128435
commit 06111d5b10
10 changed files with 329 additions and 101 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

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

View File

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

View File

@ -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:

View File

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

View File

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

View File

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

View File

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