libobs: Add high-precision sRGB support
parent
848e128435
commit
06111d5b10
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
50
libobs/obs.c
50
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();
|
||||
|
|
Loading…
Reference in New Issue