AMD/Intel capture cursor

This commit is contained in:
dec05eba 2023-07-22 02:57:38 +02:00
parent fc316750b8
commit 22a0a01553
7 changed files with 158 additions and 204 deletions

4
TODO
View File

@ -79,9 +79,7 @@ Test kms_cuda on hyprland and other wlroots based compositor to see if it works.
Support "screen" (all monitors) capture on wayland. This should be done by getting all drm fds and multiple EGL_DMA_BUF_PLANEX_FD_EXT to create one egl image with all fds combined. Support "screen" (all monitors) capture on wayland. This should be done by getting all drm fds and multiple EGL_DMA_BUF_PLANEX_FD_EXT to create one egl image with all fds combined.
Support pipewire screen capture? Support pipewire screen capture?
Support screen rotation in amd/intel/nvidia wayland. Support screen rotation.
Capture cursor on amd/intel wayland without xwayland.
When nvidia supports hardware cursor then capture the cursor. Right now the cursor is captured because it's a software cursor so it's composed on the dma buf. When nvidia supports hardware cursor then capture the cursor. Right now the cursor is captured because it's a software cursor so it's composed on the dma buf.
CPU usage is pretty high on AMD/Intel/(Nvidia(wayland)), why? opening and closing fds, creating egl, cuda association, is slow when done every frame. Test if desktop portal screencast has better performance. CPU usage is pretty high on AMD/Intel/(Nvidia(wayland)), why? opening and closing fds, creating egl, cuda association, is slow when done every frame. Test if desktop portal screencast has better performance.
Cursor on amd/intel wayland only shows up when the cursor is above xwayland applications.

View File

@ -9,7 +9,6 @@ typedef struct _XDisplay Display;
typedef struct { typedef struct {
gsr_egl *egl; gsr_egl *egl;
Display *dpy;
const char *display_to_capture; /* if this is "screen", then the entire x11 screen is captured (all displays). A copy is made of this */ const char *display_to_capture; /* if this is "screen", then the entire x11 screen is captured (all displays). A copy is made of this */
gsr_gpu_info gpu_inf; gsr_gpu_info gpu_inf;
const char *card_path; /* reference */ const char *card_path; /* reference */

View File

@ -31,6 +31,11 @@ typedef struct {
uint64_t modifier; uint64_t modifier;
uint32_t connector_id; /* 0 if unknown */ uint32_t connector_id; /* 0 if unknown */
bool is_combined_plane; bool is_combined_plane;
bool is_cursor;
int x;
int y;
int src_w;
int src_h;
} gsr_kms_response_fd; } gsr_kms_response_fd;
typedef struct { typedef struct {

View File

@ -85,7 +85,28 @@ static bool connector_get_property_by_name(int drmfd, drmModeConnectorPtr props,
return false; return false;
} }
static bool plane_is_cursor_plane(int drmfd, uint32_t plane_id) { typedef enum {
PLANE_PROPERTY_X = 1 << 0,
PLANE_PROPERTY_Y = 1 << 1,
PLANE_PROPERTY_SRC_X = 1 << 2,
PLANE_PROPERTY_SRC_Y = 1 << 3,
PLANE_PROPERTY_SRC_W = 1 << 4,
PLANE_PROPERTY_SRC_H = 1 << 5,
PLANE_PROPERTY_IS_CURSOR = 1 << 6,
} plane_property_mask;
/* Returns plane_property_mask */
static uint32_t plane_get_properties(int drmfd, uint32_t plane_id, bool *is_cursor, int *x, int *y, int *src_x, int *src_y, int *src_w, int *src_h) {
*is_cursor = false;
*x = 0;
*y = 0;
*src_x = 0;
*src_y = 0;
*src_w = 0;
*src_h = 0;
plane_property_mask property_mask = 0;
drmModeObjectPropertiesPtr props = drmModeObjectGetProperties(drmfd, plane_id, DRM_MODE_OBJECT_PLANE); drmModeObjectPropertiesPtr props = drmModeObjectGetProperties(drmfd, plane_id, DRM_MODE_OBJECT_PLANE);
if(!props) if(!props)
return false; return false;
@ -95,26 +116,42 @@ static bool plane_is_cursor_plane(int drmfd, uint32_t plane_id) {
if(!prop) if(!prop)
continue; continue;
// SRC_* values are fixed 16.16 points
const uint32_t type = prop->flags & (DRM_MODE_PROP_LEGACY_TYPE | DRM_MODE_PROP_EXTENDED_TYPE); const uint32_t type = prop->flags & (DRM_MODE_PROP_LEGACY_TYPE | DRM_MODE_PROP_EXTENDED_TYPE);
if((type & DRM_MODE_PROP_ENUM) && strcmp(prop->name, "type") == 0) { if((type & DRM_MODE_PROP_SIGNED_RANGE) && strcmp(prop->name, "CRTC_X") == 0) {
*x = (int)props->prop_values[i];
property_mask |= PLANE_PROPERTY_X;
} else if((type & DRM_MODE_PROP_SIGNED_RANGE) && strcmp(prop->name, "CRTC_Y") == 0) {
*y = (int)props->prop_values[i];
property_mask |= PLANE_PROPERTY_Y;
} else if((type & DRM_MODE_PROP_RANGE) && strcmp(prop->name, "SRC_X") == 0) {
*src_x = (int)(props->prop_values[i] >> 16);
property_mask |= PLANE_PROPERTY_SRC_X;
} else if((type & DRM_MODE_PROP_RANGE) && strcmp(prop->name, "SRC_Y") == 0) {
*src_y = (int)(props->prop_values[i] >> 16);
property_mask |= PLANE_PROPERTY_SRC_Y;
} else if((type & DRM_MODE_PROP_RANGE) && strcmp(prop->name, "SRC_W") == 0) {
*src_w = (int)(props->prop_values[i] >> 16);
property_mask |= PLANE_PROPERTY_SRC_W;
} else if((type & DRM_MODE_PROP_RANGE) && strcmp(prop->name, "SRC_H") == 0) {
*src_h = (int)(props->prop_values[i] >> 16);
property_mask |= PLANE_PROPERTY_SRC_H;
} else if((type & DRM_MODE_PROP_ENUM) && strcmp(prop->name, "type") == 0) {
const uint64_t current_enum_value = props->prop_values[i]; const uint64_t current_enum_value = props->prop_values[i];
bool is_cursor = false;
for(int j = 0; j < prop->count_enums; ++j) { for(int j = 0; j < prop->count_enums; ++j) {
if(prop->enums[j].value == current_enum_value && strcmp(prop->enums[j].name, "Cursor") == 0) { if(prop->enums[j].value == current_enum_value && strcmp(prop->enums[j].name, "Cursor") == 0) {
is_cursor = true; *is_cursor = true;
property_mask |= PLANE_PROPERTY_IS_CURSOR;
break; break;
} }
} }
drmModeFreeProperty(prop);
return is_cursor;
} }
drmModeFreeProperty(prop); drmModeFreeProperty(prop);
} }
drmModeFreeObjectProperties(props); drmModeFreeObjectProperties(props);
return false; return property_mask;
} }
/* Returns 0 if not found */ /* Returns 0 if not found */
@ -175,22 +212,27 @@ static int kms_get_plane_ids(gsr_drm *drm) {
continue; continue;
} }
if(!plane->fb_id) if(plane->fb_id) {
goto next; // TODO: Fallback to getfb(1)?
drmfb = drmModeGetFB2(drm->drmfd, plane->fb_id);
if(plane_is_cursor_plane(drm->drmfd, plane->plane_id)) if(drmfb) {
goto next; drm->plane_ids[drm->num_plane_ids] = plane->plane_id;
drm->connector_ids[drm->num_plane_ids] = get_connector_by_crtc_id(&c2crtc_map, plane->crtc_id);
// TODO: Fallback to getfb(1)? ++drm->num_plane_ids;
drmfb = drmModeGetFB2(drm->drmfd, plane->fb_id); if(drmfb)
if(drmfb) { drmModeFreeFB2(drmfb);
drm->plane_ids[drm->num_plane_ids] = plane->plane_id; }
drm->connector_ids[drm->num_plane_ids] = get_connector_by_crtc_id(&c2crtc_map, plane->crtc_id); } else {
++drm->num_plane_ids; bool is_cursor = false;
drmModeFreeFB2(drmfb); int x = 0, y = 0, src_x = 0, src_y = 0, src_w = 0, src_h = 0;
plane_get_properties(drm->drmfd, plane->plane_id, &is_cursor, &x, &y, &src_x, &src_y, &src_w, &src_h);
if(is_cursor) {
drm->plane_ids[drm->num_plane_ids] = plane->plane_id;
drm->connector_ids[drm->num_plane_ids] = 0;
++drm->num_plane_ids;
}
} }
next:
drmModeFreePlane(plane); drmModeFreePlane(plane);
} }
@ -230,6 +272,9 @@ static int kms_get_fb(gsr_drm *drm, gsr_kms_response *response) {
goto next; goto next;
} }
if(!plane->fb_id)
goto next;
drmfb = drmModeGetFB2(drm->drmfd, plane->fb_id); drmfb = drmModeGetFB2(drm->drmfd, plane->fb_id);
if(!drmfb) { if(!drmfb) {
// Commented out for now because we get here if the cursor is moved to another monitor and we dont care about the cursor // Commented out for now because we get here if the cursor is moved to another monitor and we dont care about the cursor
@ -258,6 +303,10 @@ static int kms_get_fb(gsr_drm *drm, gsr_kms_response *response) {
continue; continue;
} }
bool is_cursor = false;
int x = 0, y = 0, src_x = 0, src_y = 0, src_w = 0, src_h = 0;
plane_get_properties(drm->drmfd, plane->plane_id, &is_cursor, &x, &y, &src_x, &src_y, &src_w, &src_h);
response->fds[response->num_fds].fd = fb_fd; response->fds[response->num_fds].fd = fb_fd;
response->fds[response->num_fds].width = drmfb->width; response->fds[response->num_fds].width = drmfb->width;
response->fds[response->num_fds].height = drmfb->height; response->fds[response->num_fds].height = drmfb->height;
@ -267,6 +316,18 @@ static int kms_get_fb(gsr_drm *drm, gsr_kms_response *response) {
response->fds[response->num_fds].modifier = drmfb->modifier; response->fds[response->num_fds].modifier = drmfb->modifier;
response->fds[response->num_fds].connector_id = drm->connector_ids[i]; response->fds[response->num_fds].connector_id = drm->connector_ids[i];
response->fds[response->num_fds].is_combined_plane = drmfb_has_multiple_handles(drmfb); response->fds[response->num_fds].is_combined_plane = drmfb_has_multiple_handles(drmfb);
response->fds[response->num_fds].is_cursor = is_cursor;
if(response->fds[response->num_fds].is_cursor) {
response->fds[response->num_fds].x = x;
response->fds[response->num_fds].y = y;
response->fds[response->num_fds].src_w = 0;
response->fds[response->num_fds].src_h = 0;
} else {
response->fds[response->num_fds].x = src_x;
response->fds[response->num_fds].y = src_y;
response->fds[response->num_fds].src_w = src_w;
response->fds[response->num_fds].src_h = src_h;
}
++response->num_fds; ++response->num_fds;
next: next:

View File

@ -6,8 +6,6 @@
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <assert.h> #include <assert.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <libavutil/hwcontext.h> #include <libavutil/hwcontext.h>
#include <libavutil/hwcontext_cuda.h> #include <libavutil/hwcontext_cuda.h>
#include <libavutil/frame.h> #include <libavutil/frame.h>
@ -260,7 +258,7 @@ static gsr_kms_response_fd* find_largest_drm(gsr_kms_response *kms_response) {
gsr_kms_response_fd *largest_drm = &kms_response->fds[0]; gsr_kms_response_fd *largest_drm = &kms_response->fds[0];
for(int i = 0; i < kms_response->num_fds; ++i) { for(int i = 0; i < kms_response->num_fds; ++i) {
const int64_t size = (int64_t)kms_response->fds[i].width * (int64_t)kms_response->fds[i].height; const int64_t size = (int64_t)kms_response->fds[i].width * (int64_t)kms_response->fds[i].height;
if(size > largest_size) { if(size > largest_size && !kms_response->fds[i].is_cursor) {
largest_size = size; largest_size = size;
largest_drm = &kms_response->fds[i]; largest_drm = &kms_response->fds[i];
} }

View File

@ -2,13 +2,10 @@
#include "../../kms/client/kms_client.h" #include "../../kms/client/kms_client.h"
#include "../../include/utils.h" #include "../../include/utils.h"
#include "../../include/color_conversion.h" #include "../../include/color_conversion.h"
#include "../../include/cursor.h"
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <assert.h> #include <assert.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <libavutil/hwcontext.h> #include <libavutil/hwcontext.h>
#include <libavutil/hwcontext_vaapi.h> #include <libavutil/hwcontext_vaapi.h>
#include <libavutil/frame.h> #include <libavutil/frame.h>
@ -23,13 +20,6 @@ typedef struct {
int num_connector_ids; int num_connector_ids;
} MonitorId; } MonitorId;
typedef enum {
X11_ROT_0 = 1 << 0,
X11_ROT_90 = 1 << 1,
X11_ROT_180 = 1 << 2,
X11_ROT_270 = 1 << 3
} X11Rotation;
typedef struct { typedef struct {
gsr_capture_kms_vaapi_params params; gsr_capture_kms_vaapi_params params;
XEvent xev; XEvent xev;
@ -43,25 +33,19 @@ typedef struct {
gsr_kms_response_fd wayland_kms_data; gsr_kms_response_fd wayland_kms_data;
bool using_wayland_capture; bool using_wayland_capture;
vec2i screen_size;
vec2i capture_pos; vec2i capture_pos;
vec2i capture_size; vec2i capture_size;
bool screen_capture;
MonitorId monitor_id; MonitorId monitor_id;
VADisplay va_dpy; VADisplay va_dpy;
bool requires_rotation;
X11Rotation x11_rot;
VADRMPRIMESurfaceDescriptor prime; VADRMPRIMESurfaceDescriptor prime;
unsigned int input_texture; unsigned int input_texture;
unsigned int target_textures[2]; unsigned int target_textures[2];
unsigned int cursor_texture;
gsr_color_conversion color_conversion; gsr_color_conversion color_conversion;
gsr_cursor cursor;
} gsr_capture_kms_vaapi; } gsr_capture_kms_vaapi;
static int max_int(int a, int b) { static int max_int(int a, int b) {
@ -123,22 +107,11 @@ static bool drm_create_codec_context(gsr_capture_kms_vaapi *cap_kms, AVCodecCont
typedef struct { typedef struct {
gsr_capture_kms_vaapi *cap_kms; gsr_capture_kms_vaapi *cap_kms;
Atom randr_connector_id_atom;
const char *monitor_to_capture; const char *monitor_to_capture;
int monitor_to_capture_len; int monitor_to_capture_len;
int num_monitors; int num_monitors;
int rotation;
bool wayland;
} MonitorCallbackUserdata; } MonitorCallbackUserdata;
static bool properties_has_atom(Atom *props, int nprop, Atom atom) {
for(int i = 0; i < nprop; ++i) {
if(props[i] == atom)
return true;
}
return false;
}
static void monitor_callback(const gsr_monitor *monitor, void *userdata) { static void monitor_callback(const gsr_monitor *monitor, void *userdata) {
(void)monitor; (void)monitor;
MonitorCallbackUserdata *monitor_callback_userdata = userdata; MonitorCallbackUserdata *monitor_callback_userdata = userdata;
@ -147,44 +120,9 @@ static void monitor_callback(const gsr_monitor *monitor, void *userdata) {
if(monitor_callback_userdata->monitor_to_capture_len != monitor->name_len || memcmp(monitor_callback_userdata->monitor_to_capture, monitor->name, monitor->name_len) != 0) if(monitor_callback_userdata->monitor_to_capture_len != monitor->name_len || memcmp(monitor_callback_userdata->monitor_to_capture, monitor->name, monitor->name_len) != 0)
return; return;
if(monitor_callback_userdata->wayland) { if(monitor_callback_userdata->cap_kms->monitor_id.num_connector_ids < MAX_CONNECTOR_IDS) {
if(monitor_callback_userdata->cap_kms->monitor_id.num_connector_ids < MAX_CONNECTOR_IDS) { monitor_callback_userdata->cap_kms->monitor_id.connector_ids[monitor_callback_userdata->cap_kms->monitor_id.num_connector_ids] = monitor->connector_id;
monitor_callback_userdata->cap_kms->monitor_id.connector_ids[monitor_callback_userdata->cap_kms->monitor_id.num_connector_ids] = monitor->connector_id; ++monitor_callback_userdata->cap_kms->monitor_id.num_connector_ids;
++monitor_callback_userdata->cap_kms->monitor_id.num_connector_ids;
}
} else {
if(strcmp(monitor_callback_userdata->monitor_to_capture, "screen") == 0)
monitor_callback_userdata->rotation = monitor->crt_info->rotation;
monitor_callback_userdata->rotation = monitor->crt_info->rotation;
for(int i = 0; i < monitor->crt_info->noutput && monitor_callback_userdata->cap_kms->monitor_id.num_connector_ids < MAX_CONNECTOR_IDS; ++i) {
int nprop = 0;
Atom *props = XRRListOutputProperties(monitor_callback_userdata->cap_kms->params.dpy, monitor->crt_info->outputs[i], &nprop);
if(!props)
continue;
if(!properties_has_atom(props, nprop, monitor_callback_userdata->randr_connector_id_atom)) {
XFree(props);
continue;
}
Atom type = 0;
int format = 0;
unsigned long bytes_after = 0;
unsigned long nitems = 0;
unsigned char *prop = NULL;
XRRGetOutputProperty(monitor_callback_userdata->cap_kms->params.dpy, monitor->crt_info->outputs[i],
monitor_callback_userdata->randr_connector_id_atom,
0, 128, false, false, AnyPropertyType,
&type, &format, &nitems, &bytes_after, &prop);
if(type == XA_INTEGER && format == 32) {
monitor_callback_userdata->cap_kms->monitor_id.connector_ids[monitor_callback_userdata->cap_kms->monitor_id.num_connector_ids] = *(long*)prop;
++monitor_callback_userdata->cap_kms->monitor_id.num_connector_ids;
}
XFree(props);
}
} }
if(monitor_callback_userdata->cap_kms->monitor_id.num_connector_ids == MAX_CONNECTOR_IDS) if(monitor_callback_userdata->cap_kms->monitor_id.num_connector_ids == MAX_CONNECTOR_IDS)
@ -194,8 +132,6 @@ static void monitor_callback(const gsr_monitor *monitor, void *userdata) {
static int gsr_capture_kms_vaapi_start(gsr_capture *cap, AVCodecContext *video_codec_context) { static int gsr_capture_kms_vaapi_start(gsr_capture *cap, AVCodecContext *video_codec_context) {
gsr_capture_kms_vaapi *cap_kms = cap->priv; gsr_capture_kms_vaapi *cap_kms = cap->priv;
cap_kms->x11_rot = X11_ROT_0;
gsr_monitor monitor; gsr_monitor monitor;
cap_kms->monitor_id.num_connector_ids = 0; cap_kms->monitor_id.num_connector_ids = 0;
if(gsr_egl_start_capture(cap_kms->params.egl, cap_kms->params.display_to_capture)) { if(gsr_egl_start_capture(cap_kms->params.egl, cap_kms->params.display_to_capture)) {
@ -211,54 +147,20 @@ static int gsr_capture_kms_vaapi_start(gsr_capture *cap, AVCodecContext *video_c
return -1; return -1;
} }
void *connection = cap_kms->params.wayland ? (void*)cap_kms->params.card_path : (void*)cap_kms->params.dpy;
const gsr_connection_type connection_type = cap_kms->params.wayland ? GSR_CONNECTION_DRM : GSR_CONNECTION_X11;
MonitorCallbackUserdata monitor_callback_userdata = { MonitorCallbackUserdata monitor_callback_userdata = {
cap_kms, None, cap_kms,
cap_kms->params.display_to_capture, strlen(cap_kms->params.display_to_capture), cap_kms->params.display_to_capture, strlen(cap_kms->params.display_to_capture),
0, 0,
X11_ROT_0,
true
}; };
if(cap_kms->params.wayland) { for_each_active_monitor_output((void*)cap_kms->params.card_path, GSR_CONNECTION_DRM, monitor_callback, &monitor_callback_userdata);
for_each_active_monitor_output(connection, connection_type, monitor_callback, &monitor_callback_userdata);
cap_kms->screen_size.x = 0;
cap_kms->screen_size.y = 0;
} else {
const Atom randr_connector_id_atom = XInternAtom(cap_kms->params.dpy, "CONNECTOR_ID", False);
monitor_callback_userdata.randr_connector_id_atom = randr_connector_id_atom;
monitor_callback_userdata.wayland = false;
for_each_active_monitor_output(connection, connection_type, monitor_callback, &monitor_callback_userdata);
cap_kms->screen_size.x = WidthOfScreen(DefaultScreenOfDisplay(cap_kms->params.dpy));
cap_kms->screen_size.y = HeightOfScreen(DefaultScreenOfDisplay(cap_kms->params.dpy));
}
gsr_monitor monitor; gsr_monitor monitor;
if(strcmp(cap_kms->params.display_to_capture, "screen") == 0) { if(!get_monitor_by_name((void*)cap_kms->params.card_path, GSR_CONNECTION_DRM, cap_kms->params.display_to_capture, &monitor)) {
monitor.pos.x = 0;
monitor.pos.y = 0;
monitor.size = cap_kms->screen_size;
cap_kms->screen_capture = true;
} else if(!get_monitor_by_name(connection, connection_type, cap_kms->params.display_to_capture, &monitor)) {
fprintf(stderr, "gsr error: gsr_capture_kms_vaapi_start: failed to find monitor by name \"%s\"\n", cap_kms->params.display_to_capture); fprintf(stderr, "gsr error: gsr_capture_kms_vaapi_start: failed to find monitor by name \"%s\"\n", cap_kms->params.display_to_capture);
gsr_capture_kms_vaapi_stop(cap, video_codec_context); gsr_capture_kms_vaapi_stop(cap, video_codec_context);
return -1; return -1;
} }
// TODO: Find a better way to do this. Is this info available somewhere in drm? it should be!
// Note: workaround AMD/Intel issue. If there is one monitor enabled and it's rotated then
// the drm buf will also be rotated. This only happens when you only have one monitor enabled.
cap_kms->x11_rot = monitor_callback_userdata.rotation;
if(monitor_callback_userdata.num_monitors == 1 && cap_kms->x11_rot != X11_ROT_0) {
cap_kms->requires_rotation = true;
} else {
cap_kms->requires_rotation = false;
}
} }
cap_kms->capture_pos = monitor.pos; cap_kms->capture_pos = monitor.pos;
@ -275,16 +177,6 @@ static int gsr_capture_kms_vaapi_start(gsr_capture *cap, AVCodecContext *video_c
return -1; return -1;
} }
if(cap_kms->params.dpy && !cap_kms->params.wayland) {
if(gsr_cursor_init(&cap_kms->cursor, cap_kms->params.egl, cap_kms->params.dpy) != 0) {
gsr_capture_kms_vaapi_stop(cap, video_codec_context);
return -1;
}
gsr_cursor_change_window_target(&cap_kms->cursor, DefaultRootWindow(cap_kms->params.dpy));
gsr_cursor_update(&cap_kms->cursor, &cap_kms->xev);
}
return 0; return 0;
} }
@ -300,13 +192,6 @@ static void gsr_capture_kms_vaapi_tick(gsr_capture *cap, AVCodecContext *video_c
// TODO: // TODO:
cap_kms->params.egl->glClear(GL_COLOR_BUFFER_BIT); cap_kms->params.egl->glClear(GL_COLOR_BUFFER_BIT);
if(cap_kms->params.dpy && !cap_kms->params.wayland) {
while(XPending(cap_kms->params.dpy)) {
XNextEvent(cap_kms->params.dpy, &cap_kms->xev);
gsr_cursor_update(&cap_kms->cursor, &cap_kms->xev);
}
}
if(!cap_kms->created_hw_frame) { if(!cap_kms->created_hw_frame) {
cap_kms->created_hw_frame = true; cap_kms->created_hw_frame = true;
@ -354,6 +239,14 @@ static void gsr_capture_kms_vaapi_tick(gsr_capture *cap, AVCodecContext *video_c
cap_kms->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); cap_kms->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
cap_kms->params.egl->glBindTexture(GL_TEXTURE_2D, 0); cap_kms->params.egl->glBindTexture(GL_TEXTURE_2D, 0);
cap_kms->params.egl->glGenTextures(1, &cap_kms->cursor_texture);
cap_kms->params.egl->glBindTexture(GL_TEXTURE_2D, cap_kms->cursor_texture);
cap_kms->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
cap_kms->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
cap_kms->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
cap_kms->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
cap_kms->params.egl->glBindTexture(GL_TEXTURE_2D, 0);
if(cap_kms->prime.fourcc == FOURCC_NV12) { if(cap_kms->prime.fourcc == FOURCC_NV12) {
cap_kms->params.egl->glGenTextures(2, cap_kms->target_textures); cap_kms->params.egl->glGenTextures(2, cap_kms->target_textures);
for(int i = 0; i < 2; ++i) { for(int i = 0; i < 2; ++i) {
@ -470,7 +363,7 @@ static gsr_kms_response_fd* find_largest_drm(gsr_kms_response *kms_response) {
gsr_kms_response_fd *largest_drm = &kms_response->fds[0]; gsr_kms_response_fd *largest_drm = &kms_response->fds[0];
for(int i = 0; i < kms_response->num_fds; ++i) { for(int i = 0; i < kms_response->num_fds; ++i) {
const int64_t size = (int64_t)kms_response->fds[i].width * (int64_t)kms_response->fds[i].height; const int64_t size = (int64_t)kms_response->fds[i].width * (int64_t)kms_response->fds[i].height;
if(size > largest_size) { if(size > largest_size && !kms_response->fds[i].is_cursor) {
largest_size = size; largest_size = size;
largest_drm = &kms_response->fds[i]; largest_drm = &kms_response->fds[i];
} }
@ -478,6 +371,14 @@ static gsr_kms_response_fd* find_largest_drm(gsr_kms_response *kms_response) {
return largest_drm; return largest_drm;
} }
static gsr_kms_response_fd* find_cursor_drm(gsr_kms_response *kms_response) {
for(int i = 0; i < kms_response->num_fds; ++i) {
if(kms_response->fds[i].is_cursor)
return &kms_response->fds[i];
}
return NULL;
}
static int gsr_capture_kms_vaapi_capture(gsr_capture *cap, AVFrame *frame) { static int gsr_capture_kms_vaapi_capture(gsr_capture *cap, AVFrame *frame) {
(void)frame; (void)frame;
gsr_capture_kms_vaapi *cap_kms = cap->priv; gsr_capture_kms_vaapi *cap_kms = cap->priv;
@ -490,7 +391,7 @@ static int gsr_capture_kms_vaapi_capture(gsr_capture *cap, AVFrame *frame) {
cap_kms->kms_response.num_fds = 0; cap_kms->kms_response.num_fds = 0;
gsr_kms_response_fd *drm_fd = NULL; gsr_kms_response_fd *drm_fd = NULL;
bool requires_rotation = cap_kms->requires_rotation; gsr_kms_response_fd *cursor_drm_fd = NULL;
if(cap_kms->using_wayland_capture) { if(cap_kms->using_wayland_capture) {
gsr_egl_update(cap_kms->params.egl); gsr_egl_update(cap_kms->params.egl);
cap_kms->wayland_kms_data.fd = cap_kms->params.egl->fd; cap_kms->wayland_kms_data.fd = cap_kms->params.egl->fd;
@ -522,31 +423,25 @@ static int gsr_capture_kms_vaapi_capture(gsr_capture *cap, AVFrame *frame) {
return -1; return -1;
} }
if(cap_kms->screen_capture) { for(int i = 0; i < cap_kms->monitor_id.num_connector_ids; ++i) {
drm_fd = find_drm_by_connector_id(&cap_kms->kms_response, cap_kms->monitor_id.connector_ids[i]);
if(drm_fd)
break;
}
if(!drm_fd) {
drm_fd = find_first_combined_drm(&cap_kms->kms_response); drm_fd = find_first_combined_drm(&cap_kms->kms_response);
if(!drm_fd) if(!drm_fd)
drm_fd = find_largest_drm(&cap_kms->kms_response); drm_fd = find_largest_drm(&cap_kms->kms_response);
} else {
for(int i = 0; i < cap_kms->monitor_id.num_connector_ids; ++i) {
drm_fd = find_drm_by_connector_id(&cap_kms->kms_response, cap_kms->monitor_id.connector_ids[i]);
if(drm_fd) {
requires_rotation = cap_kms->x11_rot != X11_ROT_0;
break;
}
}
if(!drm_fd) {
drm_fd = find_first_combined_drm(&cap_kms->kms_response);
if(!drm_fd)
drm_fd = find_largest_drm(&cap_kms->kms_response);
}
} }
cursor_drm_fd = find_cursor_drm(&cap_kms->kms_response);
} }
if(!drm_fd) if(!drm_fd)
return -1; return -1;
bool capture_is_combined_plane = drm_fd->is_combined_plane || ((int)drm_fd->width == cap_kms->screen_size.x && (int)drm_fd->height == cap_kms->screen_size.y); //bool capture_is_combined_plane = drm_fd->is_combined_plane || ((int)drm_fd->width == cap_kms->screen_size.x && (int)drm_fd->height == cap_kms->screen_size.y);
// TODO: This causes a crash sometimes on steam deck, why? is it a driver bug? a vaapi pure version doesn't cause a crash. // TODO: This causes a crash sometimes on steam deck, why? is it a driver bug? a vaapi pure version doesn't cause a crash.
// Even ffmpeg kmsgrab causes this crash. The error is: // Even ffmpeg kmsgrab causes this crash. The error is:
@ -592,44 +487,40 @@ static int gsr_capture_kms_vaapi_capture(gsr_capture *cap, AVFrame *frame) {
0.0f); 0.0f);
} else { } else {
float texture_rotation = 0.0f; float texture_rotation = 0.0f;
if(requires_rotation) {
switch(cap_kms->x11_rot) {
case X11_ROT_90:
texture_rotation = M_PI*0.5f;
break;
case X11_ROT_180:
texture_rotation = M_PI;
break;
case X11_ROT_270:
texture_rotation = M_PI*1.5f;
break;
default:
texture_rotation = 0.0f;
break;
}
}
if(cap_kms->params.dpy && !cap_kms->params.wayland) {
gsr_cursor_tick(&cap_kms->cursor);
}
vec2i capture_pos = cap_kms->capture_pos; vec2i capture_pos = cap_kms->capture_pos;
vec2i capture_size = cap_kms->capture_size; vec2i capture_size = cap_kms->capture_size;
vec2i cursor_capture_pos = (vec2i){cap_kms->cursor.position.x - cap_kms->cursor.hotspot.x - capture_pos.x, cap_kms->cursor.position.y - cap_kms->cursor.hotspot.y - capture_pos.y}; capture_pos = (vec2i){drm_fd->x, drm_fd->y};
if(!capture_is_combined_plane) { capture_size = (vec2i){drm_fd->src_w, drm_fd->src_h};
capture_pos = (vec2i){0, 0};
//cursor_capture_pos = (vec2i){cap_kms->cursor.position.x - cap_kms->cursor.hotspot.x, cap_kms->cursor.position.y - cap_kms->cursor.hotspot.y};
}
gsr_color_conversion_draw(&cap_kms->color_conversion, cap_kms->input_texture, gsr_color_conversion_draw(&cap_kms->color_conversion, cap_kms->input_texture,
(vec2i){0, 0}, capture_size, (vec2i){0, 0}, capture_size,
capture_pos, capture_size, capture_pos, capture_size,
texture_rotation); texture_rotation);
if(cap_kms->params.dpy && !cap_kms->params.wayland) { if(cursor_drm_fd) {
gsr_color_conversion_draw(&cap_kms->color_conversion, cap_kms->cursor.texture_id, const intptr_t img_attr[] = {
cursor_capture_pos, (vec2i){cap_kms->cursor.size.x, cap_kms->cursor.size.y}, EGL_LINUX_DRM_FOURCC_EXT, cursor_drm_fd->pixel_format,
(vec2i){0, 0}, (vec2i){cap_kms->cursor.size.x, cap_kms->cursor.size.y}, EGL_WIDTH, cursor_drm_fd->width,
EGL_HEIGHT, cursor_drm_fd->height,
EGL_DMA_BUF_PLANE0_FD_EXT, cursor_drm_fd->fd,
EGL_DMA_BUF_PLANE0_OFFSET_EXT, cursor_drm_fd->offset,
EGL_DMA_BUF_PLANE0_PITCH_EXT, cursor_drm_fd->pitch,
EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, cursor_drm_fd->modifier & 0xFFFFFFFFULL,
EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, cursor_drm_fd->modifier >> 32ULL,
EGL_NONE
};
EGLImage image = cap_kms->params.egl->eglCreateImage(cap_kms->params.egl->egl_display, 0, EGL_LINUX_DMA_BUF_EXT, NULL, img_attr);
cap_kms->params.egl->glBindTexture(GL_TEXTURE_2D, cap_kms->cursor_texture);
cap_kms->params.egl->glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
cap_kms->params.egl->eglDestroyImage(cap_kms->params.egl->egl_display, image);
cap_kms->params.egl->glBindTexture(GL_TEXTURE_2D, 0);
vec2i cursor_size = {cursor_drm_fd->width, cursor_drm_fd->height};
gsr_color_conversion_draw(&cap_kms->color_conversion, cap_kms->cursor_texture,
(vec2i){cursor_drm_fd->x, cursor_drm_fd->y}, cursor_size,
(vec2i){0, 0}, cursor_size,
0.0f); 0.0f);
} }
} }
@ -651,7 +542,6 @@ static int gsr_capture_kms_vaapi_capture(gsr_capture *cap, AVFrame *frame) {
static void gsr_capture_kms_vaapi_stop(gsr_capture *cap, AVCodecContext *video_codec_context) { static void gsr_capture_kms_vaapi_stop(gsr_capture *cap, AVCodecContext *video_codec_context) {
gsr_capture_kms_vaapi *cap_kms = cap->priv; gsr_capture_kms_vaapi *cap_kms = cap->priv;
gsr_cursor_deinit(&cap_kms->cursor);
gsr_color_conversion_deinit(&cap_kms->color_conversion); gsr_color_conversion_deinit(&cap_kms->color_conversion);
for(uint32_t i = 0; i < cap_kms->prime.num_objects; ++i) { for(uint32_t i = 0; i < cap_kms->prime.num_objects; ++i) {
@ -667,6 +557,11 @@ static void gsr_capture_kms_vaapi_stop(gsr_capture *cap, AVCodecContext *video_c
cap_kms->input_texture = 0; cap_kms->input_texture = 0;
} }
if(cap_kms->cursor_texture) {
cap_kms->params.egl->glDeleteTextures(1, &cap_kms->cursor_texture);
cap_kms->cursor_texture = 0;
}
cap_kms->params.egl->glDeleteTextures(2, cap_kms->target_textures); cap_kms->params.egl->glDeleteTextures(2, cap_kms->target_textures);
cap_kms->target_textures[0] = 0; cap_kms->target_textures[0] = 0;
cap_kms->target_textures[1] = 0; cap_kms->target_textures[1] = 0;

View File

@ -1389,8 +1389,7 @@ int main(int argc, char **argv) {
follow_focused = true; follow_focused = true;
} else if(contains_non_hex_number(window_str)) { } else if(contains_non_hex_number(window_str)) {
// TODO: wayland, not only drm (if wlroots) if(wayland || gpu_inf.vendor != GSR_GPU_VENDOR_NVIDIA) {
if(wayland) {
if(gsr_egl_supports_wayland_capture(&egl)) { if(gsr_egl_supports_wayland_capture(&egl)) {
gsr_monitor gmon; gsr_monitor gmon;
if(!get_monitor_by_name(&egl, GSR_CONNECTION_WAYLAND, window_str, &gmon)) { if(!get_monitor_by_name(&egl, GSR_CONNECTION_WAYLAND, window_str, &gmon)) {
@ -1472,7 +1471,6 @@ int main(int argc, char **argv) {
gsr_capture_kms_vaapi_params kms_params; gsr_capture_kms_vaapi_params kms_params;
kms_params.egl = &egl; kms_params.egl = &egl;
kms_params.dpy = dpy;
kms_params.display_to_capture = capture_target; kms_params.display_to_capture = capture_target;
kms_params.gpu_inf = gpu_inf; kms_params.gpu_inf = gpu_inf;
kms_params.card_path = card_path; kms_params.card_path = card_path;