diff options
Diffstat (limited to 'src/capture/kms.c')
-rw-r--r-- | src/capture/kms.c | 116 |
1 files changed, 94 insertions, 22 deletions
diff --git a/src/capture/kms.c b/src/capture/kms.c index 829fc6a..ae0c36f 100644 --- a/src/capture/kms.c +++ b/src/capture/kms.c @@ -2,6 +2,7 @@ #include "../../include/utils.h" #include "../../include/color_conversion.h" #include "../../include/cursor.h" +#include "../../include/window/window.h" #include "../../kms/client/kms_client.h" #include <stdlib.h> @@ -17,6 +18,8 @@ #include <libavutil/mastering_display_metadata.h> #include <libavformat/avformat.h> +#define FIND_CRTC_BY_NAME_TIMEOUT_SECONDS 2.0 + #define HDMI_STATIC_METADATA_TYPE1 0 #define HDMI_EOTF_SMPTE_ST2084 2 @@ -55,6 +58,7 @@ typedef struct { AVCodecContext *video_codec_context; bool performance_error_shown; bool fast_path_failed; + bool mesa_supports_compute_only_vaapi_copy; //int drm_fd; //uint64_t prev_sequence; @@ -62,6 +66,8 @@ typedef struct { vec2i prev_target_pos; vec2i prev_plane_size; + + double last_time_monitor_check; } gsr_capture_kms; static void gsr_capture_kms_cleanup_kms_fds(gsr_capture_kms *self) { @@ -183,17 +189,19 @@ static int gsr_capture_kms_start(gsr_capture *cap, AVCodecContext *video_codec_c if(kms_init_res != 0) return kms_init_res; - self->is_x11 = gsr_egl_get_display_server(self->params.egl) == GSR_DISPLAY_SERVER_X11; + self->is_x11 = gsr_window_get_display_server(self->params.egl->window) == GSR_DISPLAY_SERVER_X11; const gsr_connection_type connection_type = self->is_x11 ? GSR_CONNECTION_X11 : GSR_CONNECTION_DRM; - if(self->is_x11) - gsr_cursor_init(&self->x11_cursor, self->params.egl, self->params.egl->x11.dpy); + if(self->is_x11) { + Display *display = gsr_window_get_display(self->params.egl->window); + gsr_cursor_init(&self->x11_cursor, self->params.egl, display); + } MonitorCallbackUserdata monitor_callback_userdata = { &self->monitor_id, self->params.display_to_capture, strlen(self->params.display_to_capture), 0, }; - for_each_active_monitor_output(self->params.egl, connection_type, monitor_callback, &monitor_callback_userdata); + for_each_active_monitor_output(self->params.egl->window, self->params.egl->card_path, connection_type, monitor_callback, &monitor_callback_userdata); if(!get_monitor_by_name(self->params.egl, connection_type, self->params.display_to_capture, &monitor)) { fprintf(stderr, "gsr error: gsr_capture_kms_start: failed to find monitor by name \"%s\"\n", self->params.display_to_capture); @@ -202,7 +210,7 @@ static int gsr_capture_kms_start(gsr_capture *cap, AVCodecContext *video_codec_c } monitor.name = self->params.display_to_capture; - self->monitor_rotation = drm_monitor_get_display_server_rotation(self->params.egl, &monitor); + self->monitor_rotation = drm_monitor_get_display_server_rotation(self->params.egl->window, &monitor); self->capture_pos = monitor.pos; /* Monitor size is already rotated on x11 when the monitor is rotated, no need to apply it ourselves */ @@ -224,10 +232,22 @@ static int gsr_capture_kms_start(gsr_capture *cap, AVCodecContext *video_codec_c video_codec_context->height = FFALIGN(self->params.output_resolution.y, 2); } + self->fast_path_failed = self->params.egl->gpu_info.vendor == GSR_GPU_VENDOR_AMD && !gl_driver_version_greater_than(&self->params.egl->gpu_info, 24, 0, 9); + 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; frame->height = video_codec_context->height; self->video_codec_context = video_codec_context; + self->last_time_monitor_check = clock_get_monotonic_seconds(); return 0; } @@ -236,7 +256,7 @@ static void gsr_capture_kms_on_event(gsr_capture *cap, gsr_egl *egl) { if(!self->is_x11) return; - XEvent *xev = gsr_egl_get_event_data(egl); + XEvent *xev = gsr_window_get_event_data(egl->window); gsr_cursor_on_event(&self->x11_cursor, xev); } @@ -421,7 +441,7 @@ static gsr_kms_response_item* find_monitor_drm(gsr_capture_kms *self, bool *capt } // Will never happen on wayland unless the target monitor has been disconnected - if(!drm_fd) { + if(!drm_fd && self->is_x11) { drm_fd = find_largest_drm(&self->kms_response); *capture_is_combined_plane = true; } @@ -507,7 +527,7 @@ static void render_drm_cursor(gsr_capture_kms *self, gsr_color_conversion *color gsr_color_conversion_draw(color_conversion, self->cursor_texture_id, cursor_pos, (vec2i){cursor_size.x * scale.x, cursor_size.y * scale.y}, (vec2i){0, 0}, cursor_size, - texture_rotation, cursor_texture_id_is_external); + texture_rotation, cursor_texture_id_is_external, GSR_SOURCE_COLOR_RGB); self->params.egl->glDisable(GL_SCISSOR_TEST); } @@ -521,7 +541,8 @@ static void render_x11_cursor(gsr_capture_kms *self, gsr_color_conversion *color self->capture_size.y == 0 ? 0 : (double)output_size.y / (double)self->capture_size.y }; - gsr_cursor_tick(&self->x11_cursor, DefaultRootWindow(self->params.egl->x11.dpy)); + Display *display = gsr_window_get_display(self->params.egl->window); + gsr_cursor_tick(&self->x11_cursor, DefaultRootWindow(display)); const vec2i cursor_pos = { target_pos.x + (self->x11_cursor.position.x - self->x11_cursor.hotspot.x - capture_pos.x) * scale.x, @@ -534,7 +555,7 @@ static void render_x11_cursor(gsr_capture_kms *self, gsr_color_conversion *color gsr_color_conversion_draw(color_conversion, self->x11_cursor.texture_id, cursor_pos, (vec2i){self->x11_cursor.size.x * scale.x, self->x11_cursor.size.y * scale.y}, (vec2i){0, 0}, self->x11_cursor.size, - 0.0f, false); + 0.0f, false, GSR_SOURCE_COLOR_RGB); self->params.egl->glDisable(GL_SCISSOR_TEST); } @@ -547,6 +568,55 @@ static void gsr_capture_kms_update_capture_size_change(gsr_capture_kms *self, gs } } +static void gsr_capture_kms_update_connector_ids(gsr_capture_kms *self) { + const double now = clock_get_monotonic_seconds(); + if(now - self->last_time_monitor_check < FIND_CRTC_BY_NAME_TIMEOUT_SECONDS) + return; + + self->last_time_monitor_check = now; + /* TODO: Assume for now that there is only 1 framebuffer for all monitors and it doesn't change */ + if(self->is_x11) + return; + + self->monitor_id.num_connector_ids = 0; + const gsr_connection_type connection_type = self->is_x11 ? GSR_CONNECTION_X11 : GSR_CONNECTION_DRM; + // MonitorCallbackUserdata monitor_callback_userdata = { + // &self->monitor_id, + // self->params.display_to_capture, strlen(self->params.display_to_capture), + // 0, + // }; + // for_each_active_monitor_output(self->params.egl->window, self->params.egl->card_path, connection_type, monitor_callback, &monitor_callback_userdata); + + gsr_monitor monitor; + if(!get_monitor_by_name(self->params.egl, connection_type, self->params.display_to_capture, &monitor)) { + fprintf(stderr, "gsr error: gsr_capture_kms_update_connector_ids: failed to find monitor by name \"%s\"\n", self->params.display_to_capture); + return; + } + + self->monitor_id.num_connector_ids = 1; + self->monitor_id.connector_ids[0] = monitor.connector_id; + + monitor.name = self->params.display_to_capture; + self->monitor_rotation = drm_monitor_get_display_server_rotation(self->params.egl->window, &monitor); + + self->capture_pos = monitor.pos; + /* Monitor size is already rotated on x11 when the monitor is rotated, no need to apply it ourselves */ + if(self->is_x11) + self->capture_size = monitor.size; + else + self->capture_size = rotate_capture_size_if_rotated(self, monitor.size); +} + +static void gsr_capture_kms_fail_fast_path_if_not_fast(gsr_capture_kms *self, uint32_t pixel_format) { + const uint8_t pixel_format_color_depth_1 = (pixel_format >> 16) & 0xFF; + if(!self->fast_path_failed && self->params.egl->gpu_info.vendor == GSR_GPU_VENDOR_AMD && !self->mesa_supports_compute_only_vaapi_copy && (pixel_format_color_depth_1 == '3' || pixel_format_color_depth_1 == '4')) { + self->fast_path_failed = true; + fprintf(stderr, "gsr warning: gsr_capture_kms_capture: the monitor you are recording is in 10/12-bit color format and your mesa version is <= 24.3.6, composition will be used." + " If you experience performance problems in the video then record on a single window on X11 or use portal capture option instead or disable 10/12-bit color option in your desktop environment settings," + " or try to record the monitor on X11 instead (if you aren't already doing that) or update your mesa version.\n"); + } +} + static int gsr_capture_kms_capture(gsr_capture *cap, AVFrame *frame, gsr_color_conversion *color_conversion) { gsr_capture_kms *self = cap->priv; @@ -566,6 +636,8 @@ static int gsr_capture_kms_capture(gsr_capture *cap, AVFrame *frame, gsr_color_c return -1; } + gsr_capture_kms_update_connector_ids(self); + bool capture_is_combined_plane = false; const gsr_kms_response_item *drm_fd = find_monitor_drm(self, &capture_is_combined_plane); if(!drm_fd) { @@ -578,17 +650,21 @@ static int gsr_capture_kms_capture(gsr_capture *cap, AVFrame *frame, gsr_color_c if(!self->performance_error_shown && self->monitor_rotation != GSR_MONITOR_ROT_0 && video_codec_context_is_vaapi(self->video_codec_context) && self->params.egl->gpu_info.vendor == GSR_GPU_VENDOR_AMD) { self->performance_error_shown = true; - fprintf(stderr,"gsr warning: gsr_capture_kms_capture: the monitor you are recording is rotated, composition will have to be used." - " If you are experience performance problems in the video then record a single window on X11 or use portal capture option instead\n"); + self->fast_path_failed = true; + fprintf(stderr, "gsr warning: gsr_capture_kms_capture: the monitor you are recording is rotated, composition will have to be used." + " If you experience performance problems in the video then record a single window on X11 or use portal capture option instead\n"); } + gsr_capture_kms_fail_fast_path_if_not_fast(self, drm_fd->pixel_format); + + self->capture_size = rotate_capture_size_if_rotated(self, (vec2i){ drm_fd->src_w, drm_fd->src_h }); + const bool is_scaled = self->params.output_resolution.x > 0 && self->params.output_resolution.y > 0; vec2i output_size = is_scaled ? self->params.output_resolution : self->capture_size; output_size = scale_keep_aspect_ratio(self->capture_size, output_size); const float texture_rotation = monitor_rotation_to_radians(self->monitor_rotation); const vec2i target_pos = { max_int(0, frame->width / 2 - output_size.x / 2), max_int(0, frame->height / 2 - output_size.y / 2) }; - self->capture_size = rotate_capture_size_if_rotated(self, (vec2i){ drm_fd->src_w, drm_fd->src_h }); gsr_capture_kms_update_capture_size_change(self, color_conversion, target_pos, drm_fd); vec2i capture_pos = self->capture_pos; @@ -628,13 +704,15 @@ static int gsr_capture_kms_capture(gsr_capture *cap, AVFrame *frame, gsr_color_c gsr_color_conversion_draw(color_conversion, self->external_texture_fallback ? self->external_input_texture_id : self->input_texture_id, target_pos, output_size, capture_pos, self->capture_size, - texture_rotation, self->external_texture_fallback); + texture_rotation, self->external_texture_fallback, GSR_SOURCE_COLOR_RGB); } if(self->params.record_cursor) { gsr_kms_response_item *cursor_drm_fd = find_cursor_drm_if_on_monitor(self, drm_fd->connector_id, capture_is_combined_plane); // The cursor is handled by x11 on x11 instead of using the cursor drm plane because on prime systems with a dedicated nvidia gpu // the cursor plane is not available when the cursor is on the monitor controlled by the nvidia device. + // TODO: This doesn't work properly with software cursor on x11 since it will draw the x11 cursor on top of the cursor already in the framebuffer. + // Detect if software cursor is used on x11 somehow. if(self->is_x11) { const vec2i cursor_monitor_offset = self->capture_pos; render_x11_cursor(self, color_conversion, cursor_monitor_offset, target_pos, output_size); @@ -658,11 +736,6 @@ static bool gsr_capture_kms_should_stop(gsr_capture *cap, bool *err) { return false; } -static gsr_source_color gsr_capture_kms_get_source_color(gsr_capture *cap) { - (void)cap; - return GSR_SOURCE_COLOR_RGB; -} - static bool gsr_capture_kms_uses_external_image(gsr_capture *cap) { (void)cap; return true; @@ -688,8 +761,8 @@ static bool gsr_capture_kms_set_hdr_metadata(gsr_capture *cap, AVMasteringDispla mastering_display_metadata->min_luminance = av_make_q(self->hdr_metadata.hdmi_metadata_type1.min_display_mastering_luminance, 10000); mastering_display_metadata->max_luminance = av_make_q(self->hdr_metadata.hdmi_metadata_type1.max_display_mastering_luminance, 1); - mastering_display_metadata->has_primaries = mastering_display_metadata->display_primaries[0][0].num > 0; - mastering_display_metadata->has_luminance = mastering_display_metadata->max_luminance.num > 0; + mastering_display_metadata->has_primaries = true; + mastering_display_metadata->has_luminance = true; return true; } @@ -749,7 +822,6 @@ gsr_capture* gsr_capture_kms_create(const gsr_capture_kms_params *params) { //.tick = gsr_capture_kms_tick, .should_stop = gsr_capture_kms_should_stop, .capture = gsr_capture_kms_capture, - .get_source_color = gsr_capture_kms_get_source_color, .uses_external_image = gsr_capture_kms_uses_external_image, .set_hdr_metadata = gsr_capture_kms_set_hdr_metadata, //.is_damaged = gsr_capture_kms_is_damaged, |