From a3b9b89a7fd6dbde224d158d38a63b7210092e72 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Sun, 9 Feb 2025 06:51:22 +0100 Subject: Attempt to fix incorrect hdr colors on kde plasma 6.2 --- src/capture/kms.c | 5 ++++ src/color_conversion.c | 62 +++++++++++++++++++++++++++++---------------- src/main.cpp | 28 ++++++++++++++++++++ src/utils.c | 2 +- src/window/window_wayland.c | 9 +++++++ 5 files changed, 83 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/capture/kms.c b/src/capture/kms.c index 9e2588b..031ded5 100644 --- a/src/capture/kms.c +++ b/src/capture/kms.c @@ -236,6 +236,11 @@ static int gsr_capture_kms_start(gsr_capture *cap, AVCodecContext *video_codec_c if(self->fast_path_failed) fprintf(stderr, "gsr warning: gsr_capture_kms_start: your amd driver (mesa) version is known to be buggy (<= version 24.0.9), falling back to opengl copy\n"); + if(self->params.hdr) { + self->fast_path_failed = true; + fprintf(stderr, "gsr warning: gsr_capture_kms_start: recording with hdr requires shader color conversion which might be slow. If this is an issue record with -w portal instead (which converts HDR to SDR)\n"); + } + self->mesa_supports_compute_only_vaapi_copy = self->params.egl->gpu_info.vendor == GSR_GPU_VENDOR_AMD && gl_driver_version_greater_than(&self->params.egl->gpu_info, 24, 3, 6); frame->width = video_codec_context->width; diff --git a/src/color_conversion.c b/src/color_conversion.c index 65f3775..de182a0 100644 --- a/src/color_conversion.c +++ b/src/color_conversion.c @@ -75,7 +75,7 @@ static const char* color_format_range_get_transform_matrix(gsr_destination_color return NULL; } -static int load_shader_y(gsr_shader *shader, gsr_egl *egl, gsr_color_uniforms *uniforms, gsr_destination_color color_format, gsr_color_range color_range, bool external_texture) { +static int load_shader_y(gsr_shader *shader, gsr_egl *egl, gsr_color_uniforms *uniforms, gsr_destination_color color_format, gsr_color_range color_range, bool external_texture, bool kde_gamma_correction) { const char *color_transform_matrix = color_format_range_get_transform_matrix(color_format, color_range); char vertex_shader[2048]; @@ -93,6 +93,19 @@ static int load_shader_y(gsr_shader *shader, gsr_egl *egl, gsr_color_uniforms *u " gl_Position = vec4(offset.x, offset.y, 0.0, 0.0) + vec4(pos.x, pos.y, 0.0, 1.0); \n" "} \n"); + const char *main_code = NULL; + if(kde_gamma_correction) { + main_code = + " vec4 pixel = texture(tex1, texcoords_out); \n" + " FragColor.x = pow((RGBtoYUV * vec4(pixel.rgb, 1.0)).x, 0.55)*0.8; \n" + " FragColor.w = pixel.a; \n"; + } else { + main_code = + " vec4 pixel = texture(tex1, texcoords_out); \n" + " FragColor.x = (RGBtoYUV * vec4(pixel.rgb, 1.0)).x; \n" + " FragColor.w = pixel.a; \n"; + } + char fragment_shader[2048]; if(external_texture) { snprintf(fragment_shader, sizeof(fragment_shader), @@ -106,10 +119,8 @@ static int load_shader_y(gsr_shader *shader, gsr_egl *egl, gsr_color_uniforms *u "%s" "void main() \n" "{ \n" - " vec4 pixel = texture(tex1, texcoords_out); \n" - " FragColor.x = (RGBtoYUV * vec4(pixel.rgb, 1.0)).x; \n" - " FragColor.w = pixel.a; \n" - "} \n", color_transform_matrix); + "%s" + "} \n", color_transform_matrix, main_code); } else { snprintf(fragment_shader, sizeof(fragment_shader), "#version 300 es \n" @@ -120,10 +131,8 @@ static int load_shader_y(gsr_shader *shader, gsr_egl *egl, gsr_color_uniforms *u "%s" "void main() \n" "{ \n" - " vec4 pixel = texture(tex1, texcoords_out); \n" - " FragColor.x = (RGBtoYUV * vec4(pixel.rgb, 1.0)).x; \n" - " FragColor.w = pixel.a; \n" - "} \n", color_transform_matrix); + "%s" + "} \n", color_transform_matrix, main_code); } if(gsr_shader_init(shader, egl, vertex_shader, fragment_shader) != 0) @@ -136,7 +145,7 @@ static int load_shader_y(gsr_shader *shader, gsr_egl *egl, gsr_color_uniforms *u return 0; } -static unsigned int load_shader_uv(gsr_shader *shader, gsr_egl *egl, gsr_color_uniforms *uniforms, gsr_destination_color color_format, gsr_color_range color_range, bool external_texture) { +static unsigned int load_shader_uv(gsr_shader *shader, gsr_egl *egl, gsr_color_uniforms *uniforms, gsr_destination_color color_format, gsr_color_range color_range, bool external_texture, bool kde_gamma_correction) { const char *color_transform_matrix = color_format_range_get_transform_matrix(color_format, color_range); char vertex_shader[2048]; @@ -154,6 +163,19 @@ static unsigned int load_shader_uv(gsr_shader *shader, gsr_egl *egl, gsr_color_u " gl_Position = (vec4(offset.x, offset.y, 0.0, 0.0) + vec4(pos.x, pos.y, 0.0, 1.0)) * vec4(0.5, 0.5, 1.0, 1.0) - vec4(0.5, 0.5, 0.0, 0.0); \n" "} \n"); + const char *main_code = NULL; + if(kde_gamma_correction) { + main_code = + " vec4 pixel = texture(tex1, texcoords_out); \n" + " FragColor.xy = (RGBtoYUV * vec4(pow(pixel.rgb, vec3(0.3)), 1.0)).yz; \n" + " FragColor.w = pixel.a; \n"; + } else { + main_code = + " vec4 pixel = texture(tex1, texcoords_out); \n" + " FragColor.xy = (RGBtoYUV * vec4(pixel.rgb, 1.0)).yz; \n" + " FragColor.w = pixel.a; \n"; + } + char fragment_shader[2048]; if(external_texture) { snprintf(fragment_shader, sizeof(fragment_shader), @@ -167,10 +189,8 @@ static unsigned int load_shader_uv(gsr_shader *shader, gsr_egl *egl, gsr_color_u "%s" "void main() \n" "{ \n" - " vec4 pixel = texture(tex1, texcoords_out); \n" - " FragColor.xy = (RGBtoYUV * vec4(pixel.rgb, 1.0)).yz; \n" - " FragColor.w = pixel.a; \n" - "} \n", color_transform_matrix); + "%s" + "} \n", color_transform_matrix, main_code); } else { snprintf(fragment_shader, sizeof(fragment_shader), "#version 300 es \n" @@ -181,10 +201,8 @@ static unsigned int load_shader_uv(gsr_shader *shader, gsr_egl *egl, gsr_color_u "%s" "void main() \n" "{ \n" - " vec4 pixel = texture(tex1, texcoords_out); \n" - " FragColor.xy = (RGBtoYUV * vec4(pixel.rgb, 1.0)).yz; \n" - " FragColor.w = pixel.a; \n" - "} \n", color_transform_matrix); + "%s" + "} \n", color_transform_matrix, main_code); } if(gsr_shader_init(shader, egl, vertex_shader, fragment_shader) != 0) @@ -261,23 +279,23 @@ int gsr_color_conversion_init(gsr_color_conversion *self, const gsr_color_conver return -1; } - if(load_shader_y(&self->shaders[0], self->params.egl, &self->uniforms[0], params->destination_color, params->color_range, false) != 0) { + if(load_shader_y(&self->shaders[0], self->params.egl, &self->uniforms[0], params->destination_color, params->color_range, false, params->kde_gamma_correction) != 0) { fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load Y shader\n"); goto err; } - if(load_shader_uv(&self->shaders[1], self->params.egl, &self->uniforms[1], params->destination_color, params->color_range, false) != 0) { + if(load_shader_uv(&self->shaders[1], self->params.egl, &self->uniforms[1], params->destination_color, params->color_range, false, params->kde_gamma_correction) != 0) { fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load UV shader\n"); goto err; } if(self->params.load_external_image_shader) { - if(load_shader_y(&self->shaders[2], self->params.egl, &self->uniforms[2], params->destination_color, params->color_range, true) != 0) { + if(load_shader_y(&self->shaders[2], self->params.egl, &self->uniforms[2], params->destination_color, params->color_range, true, params->kde_gamma_correction) != 0) { fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load Y shader\n"); goto err; } - if(load_shader_uv(&self->shaders[3], self->params.egl, &self->uniforms[3], params->destination_color, params->color_range, true) != 0) { + if(load_shader_uv(&self->shaders[3], self->params.egl, &self->uniforms[3], params->destination_color, params->color_range, true, params->kde_gamma_correction) != 0) { fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load UV shader\n"); goto err; } diff --git a/src/main.cpp b/src/main.cpp index 97b8742..6a8d0c4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3006,6 +3006,33 @@ static AudioDeviceData create_application_audio_audio_input(const MergedAudioInp } #endif +static bool is_kde_plasma_version_greater_than_6_1_90() { + FILE *f = popen("plasmashell -v 2> /dev/null", "r"); + if(!f) + return false; + + char output[512]; + const ssize_t bytes_read = fread(output, 1, sizeof(output) - 1, f); + if(bytes_read < 0 || ferror(f)) { + pclose(f); + return false; + } + output[bytes_read] = '\0'; + + bool is_above = false; + const char *version_start = strstr(output, "plasmashell "); + if(version_start) { + version_start += 12; + int major = 0; + int minor = 0; + int patch = 0; + is_above = sscanf(version_start, "%d.%d.%d", &major, &minor, &patch) == 3 && version_greater_than(major, minor, patch, 6, 1, 90); + } + + pclose(f); + return is_above; +} + static bool arg_get_boolean_value(std::map &args, const char *arg_name, bool default_value) { auto it = args.find(arg_name); if(it == args.end() || !it->second.value()) { @@ -3734,6 +3761,7 @@ int main(int argc, char **argv) { color_conversion_params.color_range = color_range; color_conversion_params.egl = &egl; color_conversion_params.load_external_image_shader = gsr_capture_uses_external_image(capture); + color_conversion_params.kde_gamma_correction = hdr && is_monitor_capture && window->is_compositor_kwin && window->is_compositor_kwin(window) && is_kde_plasma_version_greater_than_6_1_90(); gsr_video_encoder_get_textures(video_encoder, color_conversion_params.destination_textures, &color_conversion_params.num_destination_textures, &color_conversion_params.destination_color); gsr_color_conversion color_conversion; diff --git a/src/utils.c b/src/utils.c index 1feff9d..61ca856 100644 --- a/src/utils.c +++ b/src/utils.c @@ -413,7 +413,7 @@ bool gl_get_gpu_info(gsr_egl *egl, gsr_gpu_info *info) { return supported; } -static bool version_greater_than(int major, int minor, int patch, int other_major, int other_minor, int other_patch) { +bool version_greater_than(int major, int minor, int patch, int other_major, int other_minor, int other_patch) { return (major > other_major) || (major == other_major && minor > other_minor) || (major == other_major && minor == other_minor && patch > other_patch); } diff --git a/src/window/window_wayland.c b/src/window/window_wayland.c index 3a82bfa..fa9be28 100644 --- a/src/window/window_wayland.c +++ b/src/window/window_wayland.c @@ -29,6 +29,7 @@ typedef struct { void *compositor; gsr_wayland_output outputs[GSR_MAX_OUTPUTS]; int num_outputs; + bool is_compositor_kwin; } gsr_window_wayland; static void output_handle_geometry(void *data, struct wl_output *wl_output, @@ -123,6 +124,8 @@ static void registry_add_object(void *data, struct wl_registry *registry, uint32 .name = NULL, }; wl_output_add_listener(gsr_output->output, &output_listener, gsr_output); + } else if(strcmp(interface, "org_kde_plasma_shell") == 0) { + window_wayland->is_compositor_kwin = true; } } @@ -289,6 +292,11 @@ static void gsr_window_wayland_for_each_active_monitor_output_cached(const gsr_w } } +static bool gsr_window_wayland_is_compositor_kwin(const gsr_window *window) { + const gsr_window_wayland *self = window->priv; + return self->is_compositor_kwin; +} + gsr_window* gsr_window_wayland_create(void) { gsr_window *window = calloc(1, sizeof(gsr_window)); if(!window) @@ -314,6 +322,7 @@ gsr_window* gsr_window_wayland_create(void) { .get_display = gsr_window_wayland_get_display, .get_window = gsr_window_wayland_get_window, .for_each_active_monitor_output_cached = gsr_window_wayland_for_each_active_monitor_output_cached, + .is_compositor_kwin = gsr_window_wayland_is_compositor_kwin, .priv = window_wayland }; -- cgit v1.2.3-70-g09d2