aboutsummaryrefslogtreecommitdiff
path: root/src/capture
diff options
context:
space:
mode:
Diffstat (limited to 'src/capture')
-rw-r--r--src/capture/capture.c4
-rw-r--r--src/capture/kms.c116
-rw-r--r--src/capture/nvfbc.c22
-rw-r--r--src/capture/portal.c55
-rw-r--r--src/capture/xcomposite.c45
5 files changed, 163 insertions, 79 deletions
diff --git a/src/capture/capture.c b/src/capture/capture.c
index ec10854..2a4a689 100644
--- a/src/capture/capture.c
+++ b/src/capture/capture.c
@@ -34,10 +34,6 @@ int gsr_capture_capture(gsr_capture *cap, AVFrame *frame, gsr_color_conversion *
return cap->capture(cap, frame, color_conversion);
}
-gsr_source_color gsr_capture_get_source_color(gsr_capture *cap) {
- return cap->get_source_color(cap);
-}
-
bool gsr_capture_uses_external_image(gsr_capture *cap) {
if(cap->uses_external_image)
return cap->uses_external_image(cap);
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,
diff --git a/src/capture/nvfbc.c b/src/capture/nvfbc.c
index 96f3894..676d269 100644
--- a/src/capture/nvfbc.c
+++ b/src/capture/nvfbc.c
@@ -3,12 +3,14 @@
#include "../../include/egl.h"
#include "../../include/utils.h"
#include "../../include/color_conversion.h"
+#include "../../include/window/window.h"
#include <dlfcn.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
+#include <assert.h>
#include <X11/Xlib.h>
#include <libavcodec/avcodec.h>
@@ -136,7 +138,10 @@ static void set_vertical_sync_enabled(gsr_egl *egl, int enabled) {
int result = 0;
if(egl->glXSwapIntervalEXT) {
- egl->glXSwapIntervalEXT(egl->x11.dpy, egl->x11.window, enabled ? 1 : 0);
+ assert(gsr_window_get_display_server(egl->window) == GSR_DISPLAY_SERVER_X11);
+ Display *display = gsr_window_get_display(egl->window);
+ const Window window = (Window)gsr_window_get_window(egl->window);
+ egl->glXSwapIntervalEXT(display, window, enabled ? 1 : 0);
} else if(egl->glXSwapIntervalMESA) {
result = egl->glXSwapIntervalMESA(enabled ? 1 : 0);
} else if(egl->glXSwapIntervalSGI) {
@@ -219,8 +224,11 @@ static int gsr_capture_nvfbc_setup_handle(gsr_capture_nvfbc *self) {
goto error_cleanup;
}
- self->tracking_width = XWidthOfScreen(DefaultScreenOfDisplay(self->params.egl->x11.dpy));
- self->tracking_height = XHeightOfScreen(DefaultScreenOfDisplay(self->params.egl->x11.dpy));
+ assert(gsr_window_get_display_server(self->params.egl->window) == GSR_DISPLAY_SERVER_X11);
+ Display *display = gsr_window_get_display(self->params.egl->window);
+
+ self->tracking_width = XWidthOfScreen(DefaultScreenOfDisplay(display));
+ self->tracking_height = XHeightOfScreen(DefaultScreenOfDisplay(display));
self->tracking_type = strcmp(self->params.display_to_capture, "screen") == 0 ? NVFBC_TRACKING_SCREEN : NVFBC_TRACKING_OUTPUT;
if(self->tracking_type == NVFBC_TRACKING_OUTPUT) {
if(!status_params.bXRandRAvailable) {
@@ -434,7 +442,7 @@ static int gsr_capture_nvfbc_capture(gsr_capture *cap, AVFrame *frame, gsr_color
gsr_color_conversion_draw(color_conversion, self->setup_params.dwTextures[grab_params.dwTextureIndex],
target_pos, (vec2i){output_size.x, output_size.y},
(vec2i){0, 0}, frame_size,
- 0.0f, false);
+ 0.0f, false, GSR_SOURCE_COLOR_BGR);
self->params.egl->glFlush();
self->params.egl->glFinish();
@@ -442,11 +450,6 @@ static int gsr_capture_nvfbc_capture(gsr_capture *cap, AVFrame *frame, gsr_color
return 0;
}
-static gsr_source_color gsr_capture_nvfbc_get_source_color(gsr_capture *cap) {
- (void)cap;
- return GSR_SOURCE_COLOR_BGR;
-}
-
static void gsr_capture_nvfbc_destroy(gsr_capture *cap, AVCodecContext *video_codec_context) {
(void)video_codec_context;
gsr_capture_nvfbc *self = cap->priv;
@@ -492,7 +495,6 @@ gsr_capture* gsr_capture_nvfbc_create(const gsr_capture_nvfbc_params *params) {
.tick = NULL,
.should_stop = NULL,
.capture = gsr_capture_nvfbc_capture,
- .get_source_color = gsr_capture_nvfbc_get_source_color,
.uses_external_image = NULL,
.destroy = gsr_capture_nvfbc_destroy,
.priv = cap_nvfbc
diff --git a/src/capture/portal.c b/src/capture/portal.c
index b04d5e7..27486fd 100644
--- a/src/capture/portal.c
+++ b/src/capture/portal.c
@@ -3,7 +3,7 @@
#include "../../include/egl.h"
#include "../../include/utils.h"
#include "../../include/dbus.h"
-#include "../../include/pipewire.h"
+#include "../../include/pipewire_video.h"
#include <stdlib.h>
#include <stdio.h>
@@ -20,13 +20,14 @@ typedef struct {
gsr_dbus dbus;
char *session_handle;
- gsr_pipewire pipewire;
+ gsr_pipewire_video pipewire;
vec2i capture_size;
- gsr_pipewire_dmabuf_data dmabuf_data[GSR_PIPEWIRE_DMABUF_MAX_PLANES];
+ gsr_pipewire_video_dmabuf_data dmabuf_data[GSR_PIPEWIRE_VIDEO_DMABUF_MAX_PLANES];
int num_dmabuf_data;
AVCodecContext *video_codec_context;
bool fast_path_failed;
+ bool mesa_supports_compute_only_vaapi_copy;
} gsr_capture_portal;
static void gsr_capture_portal_cleanup_plane_fds(gsr_capture_portal *self) {
@@ -57,7 +58,7 @@ static void gsr_capture_portal_stop(gsr_capture_portal *self) {
gsr_capture_portal_cleanup_plane_fds(self);
- gsr_pipewire_deinit(&self->pipewire);
+ gsr_pipewire_video_deinit(&self->pipewire);
if(self->session_handle) {
free(self->session_handle);
@@ -233,8 +234,8 @@ static int gsr_capture_portal_setup_dbus(gsr_capture_portal *self, int *pipewire
}
static bool gsr_capture_portal_get_frame_dimensions(gsr_capture_portal *self) {
- gsr_pipewire_region region = {0, 0, 0, 0};
- gsr_pipewire_region cursor_region = {0, 0, 0, 0};
+ gsr_pipewire_video_region region = {0, 0, 0, 0};
+ gsr_pipewire_video_region cursor_region = {0, 0, 0, 0};
fprintf(stderr, "gsr info: gsr_capture_portal_start: waiting for pipewire negotiation\n");
const double start_time = clock_get_monotonic_seconds();
@@ -242,7 +243,7 @@ static bool gsr_capture_portal_get_frame_dimensions(gsr_capture_portal *self) {
bool uses_external_image = false;
uint32_t fourcc = 0;
uint64_t modifiers = 0;
- if(gsr_pipewire_map_texture(&self->pipewire, self->texture_map, &region, &cursor_region, self->dmabuf_data, &self->num_dmabuf_data, &fourcc, &modifiers, &uses_external_image)) {
+ if(gsr_pipewire_video_map_texture(&self->pipewire, self->texture_map, &region, &cursor_region, self->dmabuf_data, &self->num_dmabuf_data, &fourcc, &modifiers, &uses_external_image)) {
gsr_capture_portal_cleanup_plane_fds(self);
self->capture_size.x = region.width;
self->capture_size.y = region.height;
@@ -285,7 +286,7 @@ static int gsr_capture_portal_start(gsr_capture *cap, AVCodecContext *video_code
fprintf(stderr, "gsr info: gsr_capture_portal_start: setting up pipewire\n");
/* TODO: support hdr when pipewire supports it */
/* gsr_pipewire closes the pipewire fd, even on failure */
- if(!gsr_pipewire_init(&self->pipewire, pipewire_fd, pipewire_node, video_codec_context->framerate.num, self->params.record_cursor, self->params.egl)) {
+ if(!gsr_pipewire_video_init(&self->pipewire, pipewire_fd, pipewire_node, video_codec_context->framerate.num, self->params.record_cursor, self->params.egl)) {
fprintf(stderr, "gsr error: gsr_capture_portal_start: failed to setup pipewire with fd: %d, node: %" PRIu32 "\n", pipewire_fd, pipewire_node);
gsr_capture_portal_stop(self);
return -1;
@@ -310,6 +311,12 @@ static int gsr_capture_portal_start(gsr_capture *cap, AVCodecContext *video_code
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");
+
+ 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;
@@ -321,18 +328,28 @@ static int max_int(int a, int b) {
return a > b ? a : b;
}
+static void gsr_capture_portal_fail_fast_path_if_not_fast(gsr_capture_portal *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 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_portal_capture(gsr_capture *cap, AVFrame *frame, gsr_color_conversion *color_conversion) {
(void)frame;
(void)color_conversion;
gsr_capture_portal *self = cap->priv;
/* TODO: Handle formats other than RGB(a) */
- gsr_pipewire_region region = {0, 0, 0, 0};
- gsr_pipewire_region cursor_region = {0, 0, 0, 0};
+ gsr_pipewire_video_region region = {0, 0, 0, 0};
+ gsr_pipewire_video_region cursor_region = {0, 0, 0, 0};
uint32_t pipewire_fourcc = 0;
uint64_t pipewire_modifiers = 0;
bool using_external_image = false;
- if(gsr_pipewire_map_texture(&self->pipewire, self->texture_map, &region, &cursor_region, self->dmabuf_data, &self->num_dmabuf_data, &pipewire_fourcc, &pipewire_modifiers, &using_external_image)) {
+ if(gsr_pipewire_video_map_texture(&self->pipewire, self->texture_map, &region, &cursor_region, self->dmabuf_data, &self->num_dmabuf_data, &pipewire_fourcc, &pipewire_modifiers, &using_external_image)) {
if(region.width != self->capture_size.x || region.height != self->capture_size.y) {
self->capture_size.x = region.width;
self->capture_size.y = region.height;
@@ -342,6 +359,8 @@ static int gsr_capture_portal_capture(gsr_capture *cap, AVFrame *frame, gsr_colo
return 0;
}
+ gsr_capture_portal_fail_fast_path_if_not_fast(self, pipewire_fourcc);
+
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);
@@ -377,7 +396,7 @@ static int gsr_capture_portal_capture(gsr_capture *cap, AVFrame *frame, gsr_colo
gsr_color_conversion_draw(color_conversion, using_external_image ? self->texture_map.external_texture_id : self->texture_map.texture_id,
target_pos, output_size,
(vec2i){region.x, region.y}, self->capture_size,
- 0.0f, using_external_image);
+ 0.0f, using_external_image, GSR_SOURCE_COLOR_RGB);
}
if(self->params.record_cursor && self->texture_map.cursor_texture_id > 0 && cursor_region.width > 0) {
@@ -396,7 +415,7 @@ static int gsr_capture_portal_capture(gsr_capture *cap, AVFrame *frame, gsr_colo
gsr_color_conversion_draw(color_conversion, self->texture_map.cursor_texture_id,
(vec2i){cursor_pos.x, cursor_pos.y}, (vec2i){cursor_region.width * scale.x, cursor_region.height * scale.y},
(vec2i){0, 0}, (vec2i){cursor_region.width, cursor_region.height},
- 0.0f, false);
+ 0.0f, false, GSR_SOURCE_COLOR_RGB);
self->params.egl->glDisable(GL_SCISSOR_TEST);
}
@@ -408,11 +427,6 @@ static int gsr_capture_portal_capture(gsr_capture *cap, AVFrame *frame, gsr_colo
return 0;
}
-static gsr_source_color gsr_capture_portal_get_source_color(gsr_capture *cap) {
- (void)cap;
- return GSR_SOURCE_COLOR_RGB;
-}
-
static bool gsr_capture_portal_uses_external_image(gsr_capture *cap) {
(void)cap;
return true;
@@ -420,12 +434,12 @@ static bool gsr_capture_portal_uses_external_image(gsr_capture *cap) {
static bool gsr_capture_portal_is_damaged(gsr_capture *cap) {
gsr_capture_portal *self = cap->priv;
- return gsr_pipewire_is_damaged(&self->pipewire);
+ return gsr_pipewire_video_is_damaged(&self->pipewire);
}
static void gsr_capture_portal_clear_damage(gsr_capture *cap) {
gsr_capture_portal *self = cap->priv;
- gsr_pipewire_clear_damage(&self->pipewire);
+ gsr_pipewire_video_clear_damage(&self->pipewire);
}
static void gsr_capture_portal_destroy(gsr_capture *cap, AVCodecContext *video_codec_context) {
@@ -462,7 +476,6 @@ gsr_capture* gsr_capture_portal_create(const gsr_capture_portal_params *params)
.tick = NULL,
.should_stop = NULL,
.capture = gsr_capture_portal_capture,
- .get_source_color = gsr_capture_portal_get_source_color,
.uses_external_image = gsr_capture_portal_uses_external_image,
.is_damaged = gsr_capture_portal_is_damaged,
.clear_damage = gsr_capture_portal_clear_damage,
diff --git a/src/capture/xcomposite.c b/src/capture/xcomposite.c
index 6a3be16..5cef71d 100644
--- a/src/capture/xcomposite.c
+++ b/src/capture/xcomposite.c
@@ -3,6 +3,7 @@
#include "../../include/utils.h"
#include "../../include/cursor.h"
#include "../../include/color_conversion.h"
+#include "../../include/window/window.h"
#include <stdlib.h>
#include <stdio.h>
@@ -16,6 +17,7 @@
typedef struct {
gsr_capture_xcomposite_params params;
+ Display *display;
bool should_stop;
bool stop_is_error;
@@ -66,12 +68,12 @@ static int gsr_capture_xcomposite_start(gsr_capture *cap, AVCodecContext *video_
gsr_capture_xcomposite *self = cap->priv;
if(self->params.follow_focused) {
- self->net_active_window_atom = XInternAtom(self->params.egl->x11.dpy, "_NET_ACTIVE_WINDOW", False);
+ self->net_active_window_atom = XInternAtom(self->display, "_NET_ACTIVE_WINDOW", False);
if(!self->net_active_window_atom) {
fprintf(stderr, "gsr error: gsr_capture_xcomposite_start failed: failed to get _NET_ACTIVE_WINDOW atom\n");
return -1;
}
- self->window = get_focused_window(self->params.egl->x11.dpy, self->net_active_window_atom);
+ self->window = get_focused_window(self->display, self->net_active_window_atom);
} else {
self->window = self->params.window;
}
@@ -79,7 +81,7 @@ static int gsr_capture_xcomposite_start(gsr_capture *cap, AVCodecContext *video_
/* TODO: Do these in tick, and allow error if follow_focused */
XWindowAttributes attr;
- if(!XGetWindowAttributes(self->params.egl->x11.dpy, self->window, &attr) && !self->params.follow_focused) {
+ if(!XGetWindowAttributes(self->display, self->window, &attr) && !self->params.follow_focused) {
fprintf(stderr, "gsr error: gsr_capture_xcomposite_start failed: invalid window id: %lu\n", self->window);
return -1;
}
@@ -88,19 +90,19 @@ static int gsr_capture_xcomposite_start(gsr_capture *cap, AVCodecContext *video_
self->window_size.y = max_int(attr.height, 0);
if(self->params.follow_focused)
- XSelectInput(self->params.egl->x11.dpy, DefaultRootWindow(self->params.egl->x11.dpy), PropertyChangeMask);
+ XSelectInput(self->display, DefaultRootWindow(self->display), PropertyChangeMask);
// TODO: Get select and add these on top of it and then restore at the end. Also do the same in other xcomposite
- XSelectInput(self->params.egl->x11.dpy, self->window, StructureNotifyMask | ExposureMask);
+ XSelectInput(self->display, self->window, StructureNotifyMask | ExposureMask);
/* Disable vsync */
self->params.egl->eglSwapInterval(self->params.egl->egl_display, 0);
- if(window_texture_init(&self->window_texture, self->params.egl->x11.dpy, self->window, self->params.egl) != 0 && !self->params.follow_focused) {
- fprintf(stderr, "gsr error: gsr_capture_xcomposite_start: failed to get window texture for window %ld\n", self->window);
+ if(window_texture_init(&self->window_texture, self->display, self->window, self->params.egl) != 0 && !self->params.follow_focused) {
+ fprintf(stderr, "gsr error: gsr_capture_xcomposite_start: failed to get window texture for window %ld\n", (long)self->window);
return -1;
}
- if(gsr_cursor_init(&self->cursor, self->params.egl, self->params.egl->x11.dpy) != 0) {
+ if(gsr_cursor_init(&self->cursor, self->params.egl, self->display) != 0) {
gsr_capture_xcomposite_stop(self);
return -1;
}
@@ -122,6 +124,10 @@ static int gsr_capture_xcomposite_start(gsr_capture *cap, AVCodecContext *video_
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");
+
frame->width = video_codec_context->width;
frame->height = video_codec_context->height;
@@ -139,24 +145,24 @@ static void gsr_capture_xcomposite_tick(gsr_capture *cap) {
if(self->init_new_window) {
self->init_new_window = false;
- Window focused_window = get_focused_window(self->params.egl->x11.dpy, self->net_active_window_atom);
+ Window focused_window = get_focused_window(self->display, self->net_active_window_atom);
if(focused_window != self->window || !self->follow_focused_initialized) {
self->follow_focused_initialized = true;
- XSelectInput(self->params.egl->x11.dpy, self->window, 0);
+ XSelectInput(self->display, self->window, 0);
self->window = focused_window;
- XSelectInput(self->params.egl->x11.dpy, self->window, StructureNotifyMask | ExposureMask);
+ XSelectInput(self->display, self->window, StructureNotifyMask | ExposureMask);
XWindowAttributes attr;
attr.width = 0;
attr.height = 0;
- if(!XGetWindowAttributes(self->params.egl->x11.dpy, self->window, &attr))
+ if(!XGetWindowAttributes(self->display, self->window, &attr))
fprintf(stderr, "gsr error: gsr_capture_xcomposite_tick failed: invalid window id: %lu\n", self->window);
self->window_size.x = max_int(attr.width, 0);
self->window_size.y = max_int(attr.height, 0);
window_texture_deinit(&self->window_texture);
- window_texture_init(&self->window_texture, self->params.egl->x11.dpy, self->window, self->params.egl); // TODO: Do not do the below window_texture_on_resize after this
+ window_texture_init(&self->window_texture, self->display, self->window, self->params.egl); // TODO: Do not do the below window_texture_on_resize after this
self->texture_size.x = 0;
self->texture_size.y = 0;
@@ -196,7 +202,7 @@ static void gsr_capture_xcomposite_tick(gsr_capture *cap) {
static void gsr_capture_xcomposite_on_event(gsr_capture *cap, gsr_egl *egl) {
gsr_capture_xcomposite *self = cap->priv;
- XEvent *xev = gsr_egl_get_event_data(egl);
+ XEvent *xev = gsr_window_get_event_data(egl->window);
switch(xev->type) {
case DestroyNotify: {
/* Window died (when not following focused window), so we stop recording */
@@ -281,7 +287,7 @@ static int gsr_capture_xcomposite_capture(gsr_capture *cap, AVFrame *frame, gsr_
gsr_color_conversion_draw(color_conversion, window_texture_get_opengl_texture_id(&self->window_texture),
target_pos, output_size,
(vec2i){0, 0}, self->texture_size,
- 0.0f, false);
+ 0.0f, false, GSR_SOURCE_COLOR_RGB);
}
if(self->params.record_cursor && self->cursor.visible) {
@@ -303,7 +309,7 @@ static int gsr_capture_xcomposite_capture(gsr_capture *cap, AVFrame *frame, gsr_
gsr_color_conversion_draw(color_conversion, self->cursor.texture_id,
cursor_pos, (vec2i){self->cursor.size.x * scale.x, self->cursor.size.y * scale.y},
(vec2i){0, 0}, self->cursor.size,
- 0.0f, false);
+ 0.0f, false, GSR_SOURCE_COLOR_RGB);
self->params.egl->glDisable(GL_SCISSOR_TEST);
}
@@ -314,11 +320,6 @@ static int gsr_capture_xcomposite_capture(gsr_capture *cap, AVFrame *frame, gsr_
return 0;
}
-static gsr_source_color gsr_capture_xcomposite_get_source_color(gsr_capture *cap) {
- (void)cap;
- return GSR_SOURCE_COLOR_RGB;
-}
-
static uint64_t gsr_capture_xcomposite_get_window_id(gsr_capture *cap) {
gsr_capture_xcomposite *self = cap->priv;
return self->window;
@@ -351,6 +352,7 @@ gsr_capture* gsr_capture_xcomposite_create(const gsr_capture_xcomposite_params *
}
cap_xcomp->params = *params;
+ cap_xcomp->display = gsr_window_get_display(params->egl->window);
*cap = (gsr_capture) {
.start = gsr_capture_xcomposite_start,
@@ -358,7 +360,6 @@ gsr_capture* gsr_capture_xcomposite_create(const gsr_capture_xcomposite_params *
.tick = gsr_capture_xcomposite_tick,
.should_stop = gsr_capture_xcomposite_should_stop,
.capture = gsr_capture_xcomposite_capture,
- .get_source_color = gsr_capture_xcomposite_get_source_color,
.uses_external_image = NULL,
.get_window_id = gsr_capture_xcomposite_get_window_id,
.destroy = gsr_capture_xcomposite_destroy,