diff --git a/TODO b/TODO index 3232daf..33710de 100644 --- a/TODO +++ b/TODO @@ -74,8 +74,6 @@ Limit fps recording with x damage. This is good when running replay mode 24/7 an On nvidia some games apparently causes the game to appear to stutter (without dropping fps) when recording a monitor but not using when using direct screen capture. Observed in Deus Ex and Apex Legends. -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 pipewire screen capture? @@ -83,4 +81,4 @@ Support screen rotation. 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. -Fix cursor offset in kms_vaapi when capturing with multiple monitors. \ No newline at end of file +Capture is broken on amd on wlroots. It's disabled at the moment and instead uses kms capture. Find out why we get a black screen in wlroots. \ No newline at end of file diff --git a/kms/kms_shared.h b/kms/kms_shared.h index 616af43..7e326dc 100644 --- a/kms/kms_shared.h +++ b/kms/kms_shared.h @@ -14,6 +14,7 @@ typedef enum { KMS_RESULT_OK, KMS_RESULT_INVALID_REQUEST, KMS_RESULT_FAILED_TO_GET_PLANE, + KMS_RESULT_FAILED_TO_GET_PLANES, KMS_RESULT_FAILED_TO_SEND } gsr_kms_result; diff --git a/kms/server/kms_server.c b/kms/server/kms_server.c index 4184fbe..d2eb4e8 100644 --- a/kms/server/kms_server.c +++ b/kms/server/kms_server.c @@ -19,9 +19,6 @@ typedef struct { int drmfd; - uint32_t plane_ids[GSR_KMS_MAX_PLANES]; - uint32_t connector_ids[GSR_KMS_MAX_PLANES]; - size_t num_plane_ids; } gsr_drm; typedef struct { @@ -163,88 +160,6 @@ static uint32_t get_connector_by_crtc_id(const connector_to_crtc_map *c2crtc_map return 0; } -static int kms_get_plane_ids(gsr_drm *drm) { - drmModePlaneResPtr planes = NULL; - drmModeResPtr resources = NULL; - int result = -1; - - connector_to_crtc_map c2crtc_map; - c2crtc_map.num_maps = 0; - - if(drmSetClientCap(drm->drmfd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1) != 0) { - fprintf(stderr, "kms server error: drmSetClientCap DRM_CLIENT_CAP_UNIVERSAL_PLANES failed, error: %s\n", strerror(errno)); - goto error; - } - - if(drmSetClientCap(drm->drmfd, DRM_CLIENT_CAP_ATOMIC, 1) != 0) { - fprintf(stderr, "kms server warning: drmSetClientCap DRM_CLIENT_CAP_ATOMIC failed, error: %s. The wrong monitor may be captured as a result\n", strerror(errno)); - } - - planes = drmModeGetPlaneResources(drm->drmfd); - if(!planes) { - fprintf(stderr, "kms server error: failed to access planes, error: %s\n", strerror(errno)); - goto error; - } - - resources = drmModeGetResources(drm->drmfd); - if(resources) { - for(int i = 0; i < resources->count_connectors && c2crtc_map.num_maps < MAX_CONNECTORS; ++i) { - drmModeConnectorPtr connector = drmModeGetConnectorCurrent(drm->drmfd, resources->connectors[i]); - if(connector) { - uint64_t crtc_id = 0; - connector_get_property_by_name(drm->drmfd, connector, "CRTC_ID", &crtc_id); - - c2crtc_map.maps[c2crtc_map.num_maps].connector_id = connector->connector_id; - c2crtc_map.maps[c2crtc_map.num_maps].crtc_id = crtc_id; - ++c2crtc_map.num_maps; - - drmModeFreeConnector(connector); - } - } - drmModeFreeResources(resources); - } - - for(uint32_t i = 0; i < planes->count_planes && drm->num_plane_ids < GSR_KMS_MAX_PLANES; ++i) { - drmModeFB2Ptr drmfb = NULL; - drmModePlanePtr plane = drmModeGetPlane(drm->drmfd, planes->planes[i]); - if(!plane) { - fprintf(stderr, "kms server warning: failed to get drmModePlanePtr for plane %#x: %s (%d)\n", planes->planes[i], strerror(errno), errno); - continue; - } - - if(plane->fb_id) { - // TODO: Fallback to getfb(1)? - drmfb = drmModeGetFB2(drm->drmfd, plane->fb_id); - if(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); - ++drm->num_plane_ids; - if(drmfb) - drmModeFreeFB2(drmfb); - } - } else { - 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); - 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; - } - } - - drmModeFreePlane(plane); - } - - result = 0; - - error: - if(planes) - drmModeFreePlaneResources(planes); - - return result; -} - static bool drmfb_has_multiple_handles(drmModeFB2 *drmfb) { int num_handles = 0; for(uint32_t handle_index = 0; handle_index < 4 && drmfb->handles[handle_index]; ++handle_index) { @@ -253,21 +168,52 @@ static bool drmfb_has_multiple_handles(drmModeFB2 *drmfb) { return num_handles > 1; } -static int kms_get_fb(gsr_drm *drm, gsr_kms_response *response) { +static void map_crtc_to_connector_ids(gsr_drm *drm, connector_to_crtc_map *c2crtc_map) { + c2crtc_map->num_maps = 0; + drmModeResPtr resources = drmModeGetResources(drm->drmfd); + if(!resources) + return; + + for(int i = 0; i < resources->count_connectors && c2crtc_map->num_maps < MAX_CONNECTORS; ++i) { + drmModeConnectorPtr connector = drmModeGetConnectorCurrent(drm->drmfd, resources->connectors[i]); + if(!connector) + continue; + + uint64_t crtc_id = 0; + connector_get_property_by_name(drm->drmfd, connector, "CRTC_ID", &crtc_id); + + c2crtc_map->maps[c2crtc_map->num_maps].connector_id = connector->connector_id; + c2crtc_map->maps[c2crtc_map->num_maps].crtc_id = crtc_id; + ++c2crtc_map->num_maps; + + drmModeFreeConnector(connector); + } + drmModeFreeResources(resources); +} + +static int kms_get_fb(gsr_drm *drm, gsr_kms_response *response, connector_to_crtc_map *c2crtc_map) { int result = -1; response->result = KMS_RESULT_OK; response->err_msg[0] = '\0'; response->num_fds = 0; - for(size_t i = 0; i < drm->num_plane_ids && response->num_fds < GSR_KMS_MAX_PLANES; ++i) { + drmModePlaneResPtr planes = drmModeGetPlaneResources(drm->drmfd); + if(!planes) { + response->result = KMS_RESULT_FAILED_TO_GET_PLANES; + snprintf(response->err_msg, sizeof(response->err_msg), "failed to get drm planes, error: %s\n", strerror(errno)); + fprintf(stderr, "kms server error: %s\n", response->err_msg); + return result; + } + + for(uint32_t i = 0; i < planes->count_planes && response->num_fds < GSR_KMS_MAX_PLANES; ++i) { drmModePlanePtr plane = NULL; drmModeFB2 *drmfb = NULL; - plane = drmModeGetPlane(drm->drmfd, drm->plane_ids[i]); + plane = drmModeGetPlane(drm->drmfd, planes->planes[i]); if(!plane) { response->result = KMS_RESULT_FAILED_TO_GET_PLANE; - snprintf(response->err_msg, sizeof(response->err_msg), "failed to get drm plane with id %u, error: %s\n", drm->plane_ids[i], strerror(errno)); + snprintf(response->err_msg, sizeof(response->err_msg), "failed to get drm plane with id %u, error: %s\n", planes->planes[i], strerror(errno)); fprintf(stderr, "kms server error: %s\n", response->err_msg); goto next; } @@ -300,7 +246,7 @@ static int kms_get_fb(gsr_drm *drm, gsr_kms_response *response) { response->result = KMS_RESULT_FAILED_TO_GET_PLANE; snprintf(response->err_msg, sizeof(response->err_msg), "failed to get fd from drm handle, error: %s", strerror(errno)); fprintf(stderr, "kms server error: %s\n", response->err_msg); - continue; + goto next; } bool is_cursor = false; @@ -314,9 +260,9 @@ static int kms_get_fb(gsr_drm *drm, gsr_kms_response *response) { response->fds[response->num_fds].offset = drmfb->offsets[0]; response->fds[response->num_fds].pixel_format = drmfb->pixel_format; 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 = get_connector_by_crtc_id(c2crtc_map, plane->crtc_id); response->fds[response->num_fds].is_cursor = is_cursor; - response->fds[response->num_fds].is_combined_plane = !is_cursor && drmfb_has_multiple_handles(drmfb); + response->fds[response->num_fds].is_combined_plane = drmfb_has_multiple_handles(drmfb); if(is_cursor) { response->fds[response->num_fds].x = x; response->fds[response->num_fds].y = y; @@ -337,6 +283,7 @@ static int kms_get_fb(gsr_drm *drm, gsr_kms_response *response) { drmModeFreePlane(plane); } + drmModeFreePlaneResources(planes); if(response->num_fds > 0 || response->result == KMS_RESULT_OK) { result = 0; } else { @@ -382,18 +329,26 @@ int main(int argc, char **argv) { const char *card_path = argv[2]; gsr_drm drm; - drm.num_plane_ids = 0; drm.drmfd = open(card_path, O_RDONLY); if(drm.drmfd < 0) { fprintf(stderr, "kms server error: failed to open %s, error: %s", card_path, strerror(errno)); return 2; } - if(kms_get_plane_ids(&drm) != 0) { + if(drmSetClientCap(drm.drmfd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1) != 0) { + fprintf(stderr, "kms server error: drmSetClientCap DRM_CLIENT_CAP_UNIVERSAL_PLANES failed, error: %s\n", strerror(errno)); close(drm.drmfd); return 2; } + if(drmSetClientCap(drm.drmfd, DRM_CLIENT_CAP_ATOMIC, 1) != 0) { + fprintf(stderr, "kms server warning: drmSetClientCap DRM_CLIENT_CAP_ATOMIC failed, error: %s. The wrong monitor may be captured as a result\n", strerror(errno)); + } + + connector_to_crtc_map c2crtc_map; + c2crtc_map.num_maps = 0; + map_crtc_to_connector_ids(&drm, &c2crtc_map); + fprintf(stderr, "kms server info: connecting to the client\n"); bool connected = false; const double connect_timeout_sec = 5.0; @@ -458,7 +413,7 @@ int main(int argc, char **argv) { case KMS_REQUEST_TYPE_GET_KMS: { gsr_kms_response response; - if(kms_get_fb(&drm, &response) == 0) { + if(kms_get_fb(&drm, &response, &c2crtc_map) == 0) { if(send_msg_to_client(socket_fd, &response) == -1) fprintf(stderr, "kms server error: failed to respond to client KMS_REQUEST_TYPE_GET_KMS request\n"); diff --git a/src/capture/kms_vaapi.c b/src/capture/kms_vaapi.c index 00404a6..cc9aad9 100644 --- a/src/capture/kms_vaapi.c +++ b/src/capture/kms_vaapi.c @@ -388,15 +388,6 @@ static gsr_kms_response_fd* find_cursor_drm(gsr_kms_response *kms_response) { return NULL; } -static int count_non_cursor_planes(gsr_kms_response *kms_response) { - int result = 0; - for(int i = 0; i < kms_response->num_fds; ++i) { - if(!kms_response->fds[i].is_cursor) - result++; - } - return result; -} - static int gsr_capture_kms_vaapi_capture(gsr_capture *cap, AVFrame *frame) { (void)frame; gsr_capture_kms_vaapi *cap_kms = cap->priv; @@ -460,19 +451,17 @@ static int gsr_capture_kms_vaapi_capture(gsr_capture *cap, AVFrame *frame) { drm_fd = find_first_combined_drm(&cap_kms->kms_response); if(!drm_fd) drm_fd = find_largest_drm(&cap_kms->kms_response); + capture_is_combined_plane = true; } cursor_drm_fd = find_cursor_drm(&cap_kms->kms_response); - capture_is_combined_plane = (drm_fd && drm_fd->is_combined_plane) || count_non_cursor_planes(&cap_kms->kms_response) == 1; } if(!drm_fd) return -1; - /* Hide cursor when it's on another display */ - // TODO: - //if(!capture_is_combined_plane && cursor_drm_fd && cursor_drm_fd->connector_id != drm_fd->connector_id) - // cursor_drm_fd = NULL; + if(!capture_is_combined_plane && cursor_drm_fd && cursor_drm_fd->connector_id != drm_fd->connector_id) + cursor_drm_fd = NULL; // 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: diff --git a/src/egl.c b/src/egl.c index d15a3ea..3bcb6fe 100644 --- a/src/egl.c +++ b/src/egl.c @@ -536,7 +536,9 @@ void gsr_egl_unload(gsr_egl *self) { } bool gsr_egl_supports_wayland_capture(gsr_egl *self) { - return !!self->wayland.export_manager && self->wayland.num_outputs > 0; + (void)self; + return false; + //return !!self->wayland.export_manager && self->wayland.num_outputs > 0; } bool gsr_egl_start_capture(gsr_egl *self, const char *monitor_to_capture) {