From b5b4d6b2bdd1809b2e8af0b7ddec32a8d4f88e9a Mon Sep 17 00:00:00 2001 From: dec05eba Date: Mon, 22 Jul 2024 04:58:41 +0200 Subject: Fix portal capture on intel, support multiple planes in one egl image (might fix capture on intel iris) --- TODO | 4 +- include/egl.h | 15 +++++ include/pipewire.h | 9 ++- include/utils.h | 3 + kms/client/kms_client.c | 26 ++++++--- kms/kms_shared.h | 23 +++++--- kms/server/kms_server.c | 146 +++++++++++++++++++++++++++++----------------- src/capture/kms.c | 137 ++++++++++++++++++++++--------------------- src/capture/portal.c | 39 ++++++------- src/encoder/video/vaapi.c | 29 ++++----- src/pipewire.c | 87 ++++++++++++++------------- src/utils.c | 93 +++++++++++++++++++++++++++++ 12 files changed, 397 insertions(+), 214 deletions(-) diff --git a/TODO b/TODO index a878acd..a6bfd69 100644 --- a/TODO +++ b/TODO @@ -154,4 +154,6 @@ Make dbus code and pipewire setup non blocking. Support portal (pipewire) hdr capture when pipewire adds support for it. -HDR support on x11? \ No newline at end of file +HDR support on x11? + +Move most kms data to kms client. We dont need root access for everything that is server from kms server right now, such as hdr metadata and drm plane properties. Only the drm plane fd really needs root access. \ No newline at end of file diff --git a/include/egl.h b/include/egl.h index c86cb6b..c1e0aea 100644 --- a/include/egl.h +++ b/include/egl.h @@ -56,8 +56,23 @@ typedef void(*__GLXextFuncPtr)(void); #define EGL_DMA_BUF_PLANE0_FD_EXT 0x3272 #define EGL_DMA_BUF_PLANE0_OFFSET_EXT 0x3273 #define EGL_DMA_BUF_PLANE0_PITCH_EXT 0x3274 +#define EGL_DMA_BUF_PLANE1_FD_EXT 0x3275 +#define EGL_DMA_BUF_PLANE1_OFFSET_EXT 0x3276 +#define EGL_DMA_BUF_PLANE1_PITCH_EXT 0x3277 +#define EGL_DMA_BUF_PLANE2_FD_EXT 0x3278 +#define EGL_DMA_BUF_PLANE2_OFFSET_EXT 0x3279 +#define EGL_DMA_BUF_PLANE2_PITCH_EXT 0x327A +#define EGL_DMA_BUF_PLANE3_FD_EXT 0x3440 +#define EGL_DMA_BUF_PLANE3_OFFSET_EXT 0x3441 +#define EGL_DMA_BUF_PLANE3_PITCH_EXT 0x3442 #define EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT 0x3443 #define EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT 0x3444 +#define EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT 0x3445 +#define EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT 0x3446 +#define EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT 0x3447 +#define EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT 0x3448 +#define EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT 0x3449 +#define EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT 0x344A #define EGL_LINUX_DMA_BUF_EXT 0x3270 #define EGL_RED_SIZE 0x3024 #define EGL_ALPHA_SIZE 0x3021 diff --git a/include/pipewire.h b/include/pipewire.h index 2a53423..4e486fc 100644 --- a/include/pipewire.h +++ b/include/pipewire.h @@ -9,7 +9,8 @@ #include #define GSR_PIPEWIRE_MAX_MODIFIERS 1024 -#define GSR_PIPEWIRE_NUM_VIDEO_FORMATS 10 +#define GSR_PIPEWIRE_NUM_VIDEO_FORMATS 6 +#define GSR_PIPEWIRE_DMABUF_MAX_PLANES 4 typedef struct gsr_egl gsr_egl; @@ -78,7 +79,8 @@ typedef struct { gsr_pipewire_data_version server_version; gsr_pipewire_video_info video_info; - gsr_pipewire_dmabuf_data dmabuf_data; + gsr_pipewire_dmabuf_data dmabuf_data[GSR_PIPEWIRE_DMABUF_MAX_PLANES]; + size_t dmabuf_num_planes; bool started; bool stopped; @@ -95,7 +97,8 @@ typedef struct { bool gsr_pipewire_init(gsr_pipewire *self, int pipewire_fd, uint32_t pipewire_node, int fps, bool capture_cursor, gsr_egl *egl); void gsr_pipewire_deinit(gsr_pipewire *self); -bool gsr_pipewire_map_texture(gsr_pipewire *self, unsigned int texture_id, unsigned int cursor_texture_id, gsr_pipewire_region *region, gsr_pipewire_region *cursor_region, int *plane_fd); +/* |plane_fds| should be at GSR_PIPEWIRE_DMABUF_MAX_PLANES in size */ +bool gsr_pipewire_map_texture(gsr_pipewire *self, unsigned int texture_id, unsigned int cursor_texture_id, gsr_pipewire_region *region, gsr_pipewire_region *cursor_region, int *plane_fds, int *num_plane_fds); bool gsr_pipewire_recording_stopped(gsr_pipewire *self); #endif /* GSR_PIPEWIRE_H */ diff --git a/include/utils.h b/include/utils.h index c08b3f6..3921dad 100644 --- a/include/utils.h +++ b/include/utils.h @@ -44,4 +44,7 @@ bool gsr_card_path_get_render_path(const char *card_path, char *render_path); int create_directory_recursive(char *path); +/* |img_attr| needs to be at least 44 in size */ +void setup_dma_buf_attrs(intptr_t *img_attr, uint32_t format, uint32_t width, uint32_t height, const int *fds, const uint32_t *offsets, const uint32_t *pitches, const uint64_t *modifiers, int num_planes, bool use_modifier); + #endif /* GSR_UTILS_H */ diff --git a/kms/client/kms_client.c b/kms/client/kms_client.c index b579b50..966fa5e 100644 --- a/kms/client/kms_client.c +++ b/kms/client/kms_client.c @@ -36,11 +36,17 @@ static bool generate_random_characters(char *buffer, int buffer_size, const char } static void close_fds(gsr_kms_response *response) { - for(int i = 0; i < response->num_fds; ++i) { - if(response->fds[i].fd > 0) - close(response->fds[i].fd); - response->fds[i].fd = 0; + for(int i = 0; i < response->num_items; ++i) { + for(int j = 0; j < response->items[i].num_dma_bufs; ++j) { + gsr_kms_response_dma_buf *dma_buf = &response->items[i].dma_buf[j]; + if(dma_buf->fd > 0) { + close(dma_buf->fd); + dma_buf->fd = -1; + } + } + response->items[i].num_dma_bufs = 0; } + response->num_items = 0; } static int send_msg_to_server(int server_fd, gsr_kms_request *request) { @@ -82,7 +88,7 @@ static int recv_msg_from_server(int server_pid, int server_fd, gsr_kms_response response_message.msg_iov = &iov; response_message.msg_iovlen = 1; - char cmsgbuf[CMSG_SPACE(sizeof(int) * GSR_KMS_MAX_PLANES)]; + char cmsgbuf[CMSG_SPACE(sizeof(int) * GSR_KMS_MAX_ITEMS)]; memset(cmsgbuf, 0, sizeof(cmsgbuf)); response_message.msg_control = cmsgbuf; response_message.msg_controllen = sizeof(cmsgbuf); @@ -106,12 +112,16 @@ static int recv_msg_from_server(int server_pid, int server_fd, gsr_kms_response } } - if(res > 0 && response->num_fds > 0) { + if(res > 0 && response->num_items > 0) { struct cmsghdr *cmsg = CMSG_FIRSTHDR(&response_message); if(cmsg) { int *fds = (int*)CMSG_DATA(cmsg); - for(int i = 0; i < response->num_fds; ++i) { - response->fds[i].fd = fds[i]; + int fd_index = 0; + for(int i = 0; i < response->num_items; ++i) { + for(int j = 0; j < response->items[i].num_dma_bufs; ++j) { + gsr_kms_response_dma_buf *dma_buf = &response->items[i].dma_buf[j]; + dma_buf->fd = fds[fd_index++]; + } } } else { close_fds(response); diff --git a/kms/kms_shared.h b/kms/kms_shared.h index 4fa9c38..9af87e2 100644 --- a/kms/kms_shared.h +++ b/kms/kms_shared.h @@ -5,10 +5,12 @@ #include #include -#define GSR_KMS_PROTOCOL_VERSION 2 -#define GSR_KMS_MAX_PLANES 10 +#define GSR_KMS_PROTOCOL_VERSION 3 +#define GSR_KMS_MAX_ITEMS 8 +#define GSR_KMS_MAX_DMA_BUFS 4 -typedef struct gsr_kms_response_fd gsr_kms_response_fd; +typedef struct gsr_kms_response_dma_buf gsr_kms_response_dma_buf; +typedef struct gsr_kms_response_item gsr_kms_response_item; typedef struct gsr_kms_response gsr_kms_response; typedef enum { @@ -30,12 +32,17 @@ typedef struct { int new_connection_fd; } gsr_kms_request; -struct gsr_kms_response_fd { +struct gsr_kms_response_dma_buf { int fd; - uint32_t width; - uint32_t height; uint32_t pitch; uint32_t offset; +}; + +struct gsr_kms_response_item { + gsr_kms_response_dma_buf dma_buf[GSR_KMS_MAX_DMA_BUFS]; + int num_dma_bufs; + uint32_t width; + uint32_t height; uint32_t pixel_format; uint64_t modifier; uint32_t connector_id; /* 0 if unknown */ @@ -53,8 +60,8 @@ struct gsr_kms_response { uint32_t version; /* GSR_KMS_PROTOCOL_VERSION */ int result; /* gsr_kms_result */ char err_msg[128]; - gsr_kms_response_fd fds[GSR_KMS_MAX_PLANES]; - int num_fds; + gsr_kms_response_item items[GSR_KMS_MAX_ITEMS]; + int num_items; }; #endif /* #define GSR_KMS_SHARED_H */ diff --git a/kms/server/kms_server.c b/kms/server/kms_server.c index 2eaa1ed..2fdbd14 100644 --- a/kms/server/kms_server.c +++ b/kms/server/kms_server.c @@ -37,6 +37,14 @@ static int max_int(int a, int b) { return a > b ? a : b; } +static int count_num_fds(const gsr_kms_response *response) { + int num_fds = 0; + for(int i = 0; i < response->num_items; ++i) { + num_fds += response->items[i].num_dma_bufs; + } + return num_fds; +} + static int send_msg_to_client(int client_fd, gsr_kms_response *response) { struct iovec iov; iov.iov_base = response; @@ -46,21 +54,24 @@ static int send_msg_to_client(int client_fd, gsr_kms_response *response) { response_message.msg_iov = &iov; response_message.msg_iovlen = 1; - char cmsgbuf[CMSG_SPACE(sizeof(int) * max_int(1, response->num_fds))]; + char cmsgbuf[CMSG_SPACE(sizeof(int) * max_int(1, response->num_items))]; memset(cmsgbuf, 0, sizeof(cmsgbuf)); - if(response->num_fds > 0) { + if(response->num_items > 0) { response_message.msg_control = cmsgbuf; response_message.msg_controllen = sizeof(cmsgbuf); struct cmsghdr *cmsg = CMSG_FIRSTHDR(&response_message); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; - cmsg->cmsg_len = CMSG_LEN(sizeof(int) * response->num_fds); + cmsg->cmsg_len = CMSG_LEN(sizeof(int) * count_num_fds(response)); int *fds = (int*)CMSG_DATA(cmsg); - for(int i = 0; i < response->num_fds; ++i) { - fds[i] = response->fds[i].fd; + int fd_index = 0; + for(int i = 0; i < response->num_items; ++i) { + for(int j = 0; j < response->items[i].num_dma_bufs; ++j) { + fds[fd_index++] = response->items[i].dma_buf[j].fd; + } } response_message.msg_controllen = cmsg->cmsg_len; @@ -258,14 +269,27 @@ static bool get_hdr_metadata(int drm_fd, uint64_t hdr_metadata_blob_id, struct h return true; } +/* Returns the number of drm handles that we managed to get */ +static int drm_prime_handles_to_fds(gsr_drm *drm, drmModeFB2Ptr drmfb, int *fb_fds) { + for(int i = 0; i < GSR_KMS_MAX_DMA_BUFS; ++i) { + if(!drmfb->handles[i]) + return i; + + const int ret = drmPrimeHandleToFD(drm->drmfd, drmfb->handles[i], O_RDONLY, &fb_fds[i]); + if(ret != 0 || fb_fds[i] == -1) + return i; + } + return GSR_KMS_MAX_DMA_BUFS; +} + 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; + response->num_items = 0; - for(uint32_t i = 0; i < drm->planes->count_planes && response->num_fds < GSR_KMS_MAX_PLANES; ++i) { + for(uint32_t i = 0; i < drm->planes->count_planes && response->num_items < GSR_KMS_MAX_ITEMS; ++i) { drmModePlanePtr plane = NULL; drmModeFB2Ptr drmfb = NULL; @@ -299,52 +323,55 @@ static int kms_get_fb(gsr_drm *drm, gsr_kms_response *response, connector_to_crt // TODO: Check if dimensions have changed by comparing width and height to previous time this was called. // TODO: Support other plane formats than rgb (with multiple planes, such as direct YUV420 on wayland). - int fb_fd = -1; - const int ret = drmPrimeHandleToFD(drm->drmfd, drmfb->handles[0], O_RDONLY, &fb_fd); - if(ret != 0 || fb_fd == -1) { + int x = 0, y = 0, src_x = 0, src_y = 0, src_w = 0, src_h = 0; + plane_property_mask property_mask = plane_get_properties(drm->drmfd, plane->plane_id, &x, &y, &src_x, &src_y, &src_w, &src_h); + if(!(property_mask & PLANE_PROPERTY_IS_PRIMARY) && !(property_mask & PLANE_PROPERTY_IS_CURSOR)) + continue; + + int fb_fds[GSR_KMS_MAX_DMA_BUFS]; + const int num_fb_fds = drm_prime_handles_to_fds(drm, drmfb, fb_fds); + if(num_fb_fds == 0) { 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); goto cleanup_handles; } - const int fd_index = response->num_fds; + const int item_index = response->num_items; - int x = 0, y = 0, src_x = 0, src_y = 0, src_w = 0, src_h = 0; - plane_property_mask property_mask = plane_get_properties(drm->drmfd, plane->plane_id, &x, &y, &src_x, &src_y, &src_w, &src_h); - if((property_mask & PLANE_PROPERTY_IS_PRIMARY) || (property_mask & PLANE_PROPERTY_IS_CURSOR)) { - const connector_crtc_pair *crtc_pair = get_connector_pair_by_crtc_id(c2crtc_map, plane->crtc_id); - if(crtc_pair && crtc_pair->hdr_metadata_blob_id) { - response->fds[fd_index].has_hdr_metadata = get_hdr_metadata(drm->drmfd, crtc_pair->hdr_metadata_blob_id, &response->fds[fd_index].hdr_metadata); - } else { - response->fds[fd_index].has_hdr_metadata = false; - } + const connector_crtc_pair *crtc_pair = get_connector_pair_by_crtc_id(c2crtc_map, plane->crtc_id); + if(crtc_pair && crtc_pair->hdr_metadata_blob_id) { + response->items[item_index].has_hdr_metadata = get_hdr_metadata(drm->drmfd, crtc_pair->hdr_metadata_blob_id, &response->items[item_index].hdr_metadata); + } else { + response->items[item_index].has_hdr_metadata = false; + } - response->fds[fd_index].fd = fb_fd; - response->fds[fd_index].width = drmfb->width; - response->fds[fd_index].height = drmfb->height; - response->fds[fd_index].pitch = drmfb->pitches[0]; - response->fds[fd_index].offset = drmfb->offsets[0]; - response->fds[fd_index].pixel_format = drmfb->pixel_format; - response->fds[fd_index].modifier = drmfb->modifier; - response->fds[fd_index].connector_id = crtc_pair ? crtc_pair->connector_id : 0; - response->fds[fd_index].is_cursor = property_mask & PLANE_PROPERTY_IS_CURSOR; - response->fds[fd_index].is_combined_plane = false; - if(property_mask & PLANE_PROPERTY_IS_CURSOR) { - response->fds[fd_index].x = x; - response->fds[fd_index].y = y; - response->fds[fd_index].src_w = 0; - response->fds[fd_index].src_h = 0; - } else { - response->fds[fd_index].x = src_x; - response->fds[fd_index].y = src_y; - response->fds[fd_index].src_w = src_w; - response->fds[fd_index].src_h = src_h; - } - ++response->num_fds; + for(int j = 0; j < num_fb_fds; ++j) { + response->items[item_index].dma_buf[j].fd = fb_fds[j]; + response->items[item_index].dma_buf[j].pitch = drmfb->pitches[j]; + response->items[item_index].dma_buf[j].offset = drmfb->offsets[j]; + } + response->items[item_index].num_dma_bufs = num_fb_fds; + + response->items[item_index].width = drmfb->width; + response->items[item_index].height = drmfb->height; + response->items[item_index].pixel_format = drmfb->pixel_format; + response->items[item_index].modifier = drmfb->modifier; + response->items[item_index].connector_id = crtc_pair ? crtc_pair->connector_id : 0; + response->items[item_index].is_cursor = property_mask & PLANE_PROPERTY_IS_CURSOR; + response->items[item_index].is_combined_plane = false; + if(property_mask & PLANE_PROPERTY_IS_CURSOR) { + response->items[item_index].x = x; + response->items[item_index].y = y; + response->items[item_index].src_w = 0; + response->items[item_index].src_h = 0; } else { - close(fb_fd); + response->items[item_index].x = src_x; + response->items[item_index].y = src_y; + response->items[item_index].src_w = src_w; + response->items[item_index].src_h = src_h; } + ++response->num_items; cleanup_handles: drm_mode_cleanup_handles(drm->drmfd, drmfb); @@ -356,16 +383,23 @@ static int kms_get_fb(gsr_drm *drm, gsr_kms_response *response, connector_to_crt drmModeFreePlane(plane); } - if(response->num_fds > 0) + if(response->num_items > 0) response->result = KMS_RESULT_OK; if(response->result == KMS_RESULT_OK) { result = 0; } else { - for(int i = 0; i < response->num_fds; ++i) { - close(response->fds[i].fd); + for(int i = 0; i < response->num_items; ++i) { + for(int j = 0; j < response->items[i].num_dma_bufs; ++j) { + gsr_kms_response_dma_buf *dma_buf = &response->items[i].dma_buf[j]; + if(dma_buf->fd > 0) { + close(dma_buf->fd); + dma_buf->fd = -1; + } + } + response->items[i].num_dma_bufs = 0; } - response->num_fds = 0; + response->num_items = 0; } return result; @@ -506,7 +540,7 @@ int main(int argc, char **argv) { case KMS_REQUEST_TYPE_REPLACE_CONNECTION: { gsr_kms_response response; response.version = GSR_KMS_PROTOCOL_VERSION; - response.num_fds = 0; + response.num_items = 0; if(request.new_connection_fd > 0) { if(socket_fd > 0) @@ -529,7 +563,7 @@ int main(int argc, char **argv) { case KMS_REQUEST_TYPE_GET_KMS: { gsr_kms_response response; response.version = GSR_KMS_PROTOCOL_VERSION; - response.num_fds = 0; + response.num_items = 0; if(kms_get_fb(&drm, &response, &c2crtc_map) == 0) { if(send_msg_to_client(socket_fd, &response) == -1) @@ -539,9 +573,17 @@ int main(int argc, char **argv) { fprintf(stderr, "kms server error: failed to respond to client KMS_REQUEST_TYPE_GET_KMS request\n"); } - for(int i = 0; i < response.num_fds; ++i) { - close(response.fds[i].fd); + for(int i = 0; i < response.num_items; ++i) { + for(int j = 0; j < response.items[i].num_dma_bufs; ++j) { + gsr_kms_response_dma_buf *dma_buf = &response.items[i].dma_buf[j]; + if(dma_buf->fd > 0) { + close(dma_buf->fd); + dma_buf->fd = -1; + } + } + response.items[i].num_dma_bufs = 0; } + response.num_items = 0; break; } @@ -549,7 +591,7 @@ int main(int argc, char **argv) { gsr_kms_response response; response.version = GSR_KMS_PROTOCOL_VERSION; response.result = KMS_RESULT_INVALID_REQUEST; - response.num_fds = 0; + response.num_items = 0; snprintf(response.err_msg, sizeof(response.err_msg), "invalid request type %d, expected %d (%s)", request.type, KMS_REQUEST_TYPE_GET_KMS, "KMS_REQUEST_TYPE_GET_KMS"); fprintf(stderr, "kms server error: %s\n", response.err_msg); diff --git a/src/capture/kms.c b/src/capture/kms.c index 50fe01a..16d7254 100644 --- a/src/capture/kms.c +++ b/src/capture/kms.c @@ -46,12 +46,17 @@ typedef struct { } gsr_capture_kms; static void gsr_capture_kms_cleanup_kms_fds(gsr_capture_kms *self) { - for(int i = 0; i < self->kms_response.num_fds; ++i) { - if(self->kms_response.fds[i].fd > 0) - close(self->kms_response.fds[i].fd); - self->kms_response.fds[i].fd = 0; + for(int i = 0; i < self->kms_response.num_items; ++i) { + for(int j = 0; j < self->kms_response.items[i].num_dma_bufs; ++j) { + gsr_kms_response_dma_buf *dma_buf = &self->kms_response.items[i].dma_buf[j]; + if(dma_buf->fd > 0) { + close(dma_buf->fd); + dma_buf->fd = -1; + } + } + self->kms_response.items[i].num_dma_bufs = 0; } - self->kms_response.num_fds = 0; + self->kms_response.num_items = 0; } static void gsr_capture_kms_stop(gsr_capture_kms *self) { @@ -177,51 +182,51 @@ static float monitor_rotation_to_radians(gsr_monitor_rotation rot) { } /* Prefer non combined planes */ -static gsr_kms_response_fd* find_drm_by_connector_id(gsr_kms_response *kms_response, uint32_t connector_id) { +static gsr_kms_response_item* find_drm_by_connector_id(gsr_kms_response *kms_response, uint32_t connector_id) { int index_combined = -1; - for(int i = 0; i < kms_response->num_fds; ++i) { - if(kms_response->fds[i].connector_id == connector_id && !kms_response->fds[i].is_cursor) { - if(kms_response->fds[i].is_combined_plane) + for(int i = 0; i < kms_response->num_items; ++i) { + if(kms_response->items[i].connector_id == connector_id && !kms_response->items[i].is_cursor) { + if(kms_response->items[i].is_combined_plane) index_combined = i; else - return &kms_response->fds[i]; + return &kms_response->items[i]; } } if(index_combined != -1) - return &kms_response->fds[index_combined]; + return &kms_response->items[index_combined]; else return NULL; } -static gsr_kms_response_fd* find_first_combined_drm(gsr_kms_response *kms_response) { - for(int i = 0; i < kms_response->num_fds; ++i) { - if(kms_response->fds[i].is_combined_plane && !kms_response->fds[i].is_cursor) - return &kms_response->fds[i]; +static gsr_kms_response_item* find_first_combined_drm(gsr_kms_response *kms_response) { + for(int i = 0; i < kms_response->num_items; ++i) { + if(kms_response->items[i].is_combined_plane && !kms_response->items[i].is_cursor) + return &kms_response->items[i]; } return NULL; } -static gsr_kms_response_fd* find_largest_drm(gsr_kms_response *kms_response) { - if(kms_response->num_fds == 0) +static gsr_kms_response_item* find_largest_drm(gsr_kms_response *kms_response) { + if(kms_response->num_items == 0) return NULL; int64_t largest_size = 0; - gsr_kms_response_fd *largest_drm = &kms_response->fds[0]; - 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; - if(size > largest_size && !kms_response->fds[i].is_cursor) { + gsr_kms_response_item *largest_drm = &kms_response->items[0]; + for(int i = 0; i < kms_response->num_items; ++i) { + const int64_t size = (int64_t)kms_response->items[i].width * (int64_t)kms_response->items[i].height; + if(size > largest_size && !kms_response->items[i].is_cursor) { largest_size = size; - largest_drm = &kms_response->fds[i]; + largest_drm = &kms_response->items[i]; } } 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]; +static gsr_kms_response_item* find_cursor_drm(gsr_kms_response *kms_response) { + for(int i = 0; i < kms_response->num_items; ++i) { + if(kms_response->items[i].is_cursor) + return &kms_response->items[i]; } return NULL; } @@ -233,7 +238,7 @@ static bool hdr_metadata_is_supported_format(const struct hdr_output_metadata *h } // TODO: Check if this hdr data can be changed after the call to av_packet_side_data_add -static void gsr_kms_set_hdr_metadata(gsr_capture_kms *self, AVStream *video_stream, gsr_kms_response_fd *drm_fd) { +static void gsr_kms_set_hdr_metadata(gsr_capture_kms *self, AVStream *video_stream, gsr_kms_response_item *drm_fd) { if(self->hdr_metadata_set) return; @@ -314,8 +319,8 @@ static int gsr_capture_kms_capture(gsr_capture *cap, AVStream *video_stream, AVF gsr_capture_kms_cleanup_kms_fds(self); - gsr_kms_response_fd *drm_fd = NULL; - gsr_kms_response_fd *cursor_drm_fd = NULL; + gsr_kms_response_item *drm_fd = NULL; + gsr_kms_response_item *cursor_drm_fd = NULL; bool capture_is_combined_plane = false; if(gsr_kms_client_get_kms(&self->kms_client, &self->kms_response) != 0) { @@ -323,7 +328,7 @@ static int gsr_capture_kms_capture(gsr_capture *cap, AVStream *video_stream, AVF return -1; } - if(self->kms_response.num_fds == 0) { + if(self->kms_response.num_items == 0) { static bool error_shown = false; if(!error_shown) { error_shown = true; @@ -357,6 +362,14 @@ static int gsr_capture_kms_capture(gsr_capture *cap, AVStream *video_stream, AVF if(drm_fd->has_hdr_metadata && self->params.hdr && hdr_metadata_is_supported_format(&drm_fd->hdr_metadata)) gsr_kms_set_hdr_metadata(self, video_stream, drm_fd); + if(is_plane_compressed(drm_fd->modifier)) { + static bool compressed_plane_warning_shown = false; + if(!compressed_plane_warning_shown) { + compressed_plane_warning_shown = true; + fprintf(stderr, "gsr warning: gsr_capture_kms_capture: the monitor plane is compressed. The video will likely be glitched/black. Try recording on X11 instead (maybe capturing a single window) or use the \"-w portal\" capture option on Wayland.\n"); + } + } + // 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: // amdgpu: Failed to allocate a buffer: @@ -375,36 +388,21 @@ static int gsr_capture_kms_capture(gsr_capture *cap, AVStream *video_stream, AVF // Error: avcodec_send_frame failed, error: Input/output error // Assertion pic->display_order == pic->encode_order failed at libavcodec/vaapi_encode_h265.c:765 // kms server info: kms client shutdown, shutting down the server - intptr_t img_attr[18] = { - EGL_LINUX_DRM_FOURCC_EXT, drm_fd->pixel_format, - EGL_WIDTH, drm_fd->width, - EGL_HEIGHT, drm_fd->height, - EGL_DMA_BUF_PLANE0_FD_EXT, drm_fd->fd, - EGL_DMA_BUF_PLANE0_OFFSET_EXT, drm_fd->offset, - EGL_DMA_BUF_PLANE0_PITCH_EXT, drm_fd->pitch, - }; - if(is_plane_compressed(drm_fd->modifier)) { - static bool compressed_plane_warning_shown = false; - if(!compressed_plane_warning_shown) { - compressed_plane_warning_shown = true; - fprintf(stderr, "gsr warning: gsr_capture_kms_capture: the monitor plane is compressed. The video will likely be glitched/black. Try recording on X11 instead (maybe capturing a single window) or use the \"-w portal\" capture option on Wayland.\n"); - } + int fds[GSR_KMS_MAX_DMA_BUFS]; + uint32_t offsets[GSR_KMS_MAX_DMA_BUFS]; + uint32_t pitches[GSR_KMS_MAX_DMA_BUFS]; + uint64_t modifiers[GSR_KMS_MAX_DMA_BUFS]; + for(int i = 0; i < drm_fd->num_dma_bufs; ++i) { + fds[i] = drm_fd->dma_buf[i].fd; + offsets[i] = drm_fd->dma_buf[i].offset; + pitches[i] = drm_fd->dma_buf[i].pitch; + modifiers[i] = drm_fd->modifier; } - if(screen_plane_use_modifiers) { - img_attr[12] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT; - img_attr[13] = drm_fd->modifier & 0xFFFFFFFFULL; - - img_attr[14] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT; - img_attr[15] = drm_fd->modifier >> 32ULL; - - img_attr[16] = EGL_NONE; - img_attr[17] = EGL_NONE; - } else { - img_attr[12] = EGL_NONE; - img_attr[13] = EGL_NONE; - } + intptr_t img_attr[44]; + setup_dma_buf_attrs(img_attr, drm_fd->pixel_format, drm_fd->width, drm_fd->height, + fds, offsets, pitches, modifiers, drm_fd->num_dma_bufs, screen_plane_use_modifiers); EGLImage image = self->params.egl->eglCreateImage(self->params.egl->egl_display, 0, EGL_LINUX_DMA_BUF_EXT, NULL, img_attr); self->params.egl->glBindTexture(GL_TEXTURE_2D, self->input_texture_id); @@ -456,17 +454,20 @@ static int gsr_capture_kms_capture(gsr_capture *cap, AVStream *video_stream, AVF cursor_pos.x += target_x; cursor_pos.y += target_y; - const intptr_t img_attr_cursor[] = { - EGL_LINUX_DRM_FOURCC_EXT, cursor_drm_fd->pixel_format, - 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 - }; + int fds[GSR_KMS_MAX_DMA_BUFS]; + uint32_t offsets[GSR_KMS_MAX_DMA_BUFS]; + uint32_t pitches[GSR_KMS_MAX_DMA_BUFS]; + uint64_t modifiers[GSR_KMS_MAX_DMA_BUFS]; + for(int i = 0; i < cursor_drm_fd->num_dma_bufs; ++i) { + fds[i] = cursor_drm_fd->dma_buf[i].fd; + offsets[i] = cursor_drm_fd->dma_buf[i].offset; + pitches[i] = cursor_drm_fd->dma_buf[i].pitch; + modifiers[i] = cursor_drm_fd->modifier; + } + + intptr_t img_attr_cursor[44]; + setup_dma_buf_attrs(img_attr_cursor, cursor_drm_fd->pixel_format, cursor_drm_fd->width, cursor_drm_fd->height, + fds, offsets, pitches, modifiers, cursor_drm_fd->num_dma_bufs, true); EGLImage cursor_image = self->params.egl->eglCreateImage(self->params.egl->egl_display, 0, EGL_LINUX_DMA_BUF_EXT, NULL, img_attr_cursor); const int target = cursor_texture_id_is_external ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D; diff --git a/src/capture/portal.c b/src/capture/portal.c index 9bac3cf..42f9800 100644 --- a/src/capture/portal.c +++ b/src/capture/portal.c @@ -23,9 +23,20 @@ typedef struct { gsr_pipewire pipewire; vec2i capture_size; - int plane_fd; + int plane_fds[GSR_PIPEWIRE_DMABUF_MAX_PLANES]; + int num_plane_fds; } gsr_capture_portal; +static void gsr_capture_portal_cleanup_plane_fds(gsr_capture_portal *self) { + for(int i = 0; i < self->num_plane_fds; ++i) { + if(self->plane_fds[i] > 0) { + close(self->plane_fds[i]); + self->plane_fds[i] = 0; + } + } + self->num_plane_fds = 0; +} + static void gsr_capture_portal_stop(gsr_capture_portal *self) { if(self->input_texture_id) { self->params.egl->glDeleteTextures(1, &self->input_texture_id); @@ -37,10 +48,7 @@ static void gsr_capture_portal_stop(gsr_capture_portal *self) { self->cursor_texture_id = 0; } - if(self->plane_fd > 0) { - close(self->plane_fd); - self->plane_fd = 0; - } + gsr_capture_portal_cleanup_plane_fds(self); gsr_pipewire_deinit(&self->pipewire); @@ -212,13 +220,8 @@ static bool gsr_capture_portal_get_frame_dimensions(gsr_capture_portal *self) { const double start_time = clock_get_monotonic_seconds(); while(clock_get_monotonic_seconds() - start_time < 5.0) { - int plane_fd = 0; - if(gsr_pipewire_map_texture(&self->pipewire, self->input_texture_id, self->cursor_texture_id, ®ion, &cursor_region, &plane_fd)) { - if(plane_fd > 0) { - close(plane_fd); - plane_fd = 0; - } - + if(gsr_pipewire_map_texture(&self->pipewire, self->input_texture_id, self->cursor_texture_id, ®ion, &cursor_region, self->plane_fds, &self->num_plane_fds)) { + gsr_capture_portal_cleanup_plane_fds(self); self->capture_size.x = region.width; self->capture_size.y = region.height; fprintf(stderr, "gsr info: gsr_capture_portal_start: pipewire negotiation finished\n"); @@ -293,10 +296,7 @@ static int gsr_capture_portal_capture(gsr_capture *cap, AVStream *video_stream, (void)color_conversion; gsr_capture_portal *self = cap->priv; - if(self->plane_fd > 0) { - close(self->plane_fd); - self->plane_fd = 0; - } + gsr_capture_portal_cleanup_plane_fds(self); //egl->glClearColor(0.0f, 0.0f, 0.0f, 1.0f); self->params.egl->glClear(0); @@ -304,7 +304,7 @@ static int gsr_capture_portal_capture(gsr_capture *cap, AVStream *video_stream, /* TODO: Handle formats other than RGB(a) */ gsr_pipewire_region region = {0, 0, 0, 0}; gsr_pipewire_region cursor_region = {0, 0, 0, 0}; - if(gsr_pipewire_map_texture(&self->pipewire, self->input_texture_id, self->cursor_texture_id, ®ion, &cursor_region, &self->plane_fd)) { + if(gsr_pipewire_map_texture(&self->pipewire, self->input_texture_id, self->cursor_texture_id, ®ion, &cursor_region, self->plane_fds, &self->num_plane_fds)) { if(region.width != self->capture_size.x || region.height != self->capture_size.y) { gsr_color_conversion_clear(color_conversion); self->capture_size.x = region.width; @@ -351,10 +351,7 @@ static bool gsr_capture_portal_should_stop(gsr_capture *cap, bool *err) { static void gsr_capture_portal_capture_end(gsr_capture *cap, AVFrame *frame) { (void)frame; gsr_capture_portal *self = cap->priv; - if(self->plane_fd > 0) { - close(self->plane_fd); - self->plane_fd = 0; - } + gsr_capture_portal_cleanup_plane_fds(self); } static gsr_source_color gsr_capture_portal_get_source_color(gsr_capture *cap) { diff --git a/src/encoder/video/vaapi.c b/src/encoder/video/vaapi.c index 579aa93..952fab4 100644 --- a/src/encoder/video/vaapi.c +++ b/src/encoder/video/vaapi.c @@ -96,20 +96,21 @@ static bool gsr_video_encoder_vaapi_setup_textures(gsr_video_encoder_vaapi *self self->params.egl->glGenTextures(2, self->target_textures); for(int i = 0; i < 2; ++i) { const int layer = i; - const int plane = 0; - - const uint64_t modifier = self->prime.objects[self->prime.layers[layer].object_index[plane]].drm_format_modifier; - const intptr_t img_attr[] = { - EGL_LINUX_DRM_FOURCC_EXT, formats[i], - EGL_WIDTH, self->prime.width / div[i], - EGL_HEIGHT, self->prime.height / div[i], - EGL_DMA_BUF_PLANE0_FD_EXT, self->prime.objects[self->prime.layers[layer].object_index[plane]].fd, - EGL_DMA_BUF_PLANE0_OFFSET_EXT, self->prime.layers[layer].offset[plane], - EGL_DMA_BUF_PLANE0_PITCH_EXT, self->prime.layers[layer].pitch[plane], - EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, modifier & 0xFFFFFFFFULL, - EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, modifier >> 32ULL, - EGL_NONE - }; + + int fds[4]; + uint32_t offsets[4]; + uint32_t pitches[4]; + uint64_t modifiers[4]; + for(uint32_t j = 0; j < self->prime.layers[layer].num_planes; ++j) { + fds[j] = self->prime.objects[self->prime.layers[layer].object_index[j]].fd; + offsets[j] = self->prime.layers[layer].offset[j]; + pitches[j] = self->prime.layers[layer].pitch[j]; + modifiers[j] = self->prime.objects[self->prime.layers[layer].object_index[j]].drm_format_modifier; + } + + intptr_t img_attr[44]; + setup_dma_buf_attrs(img_attr, formats[i], self->prime.width / div[i], self->prime.height / div[i], + fds, offsets, pitches, modifiers, self->prime.layers[layer].num_planes, true); while(self->params.egl->eglGetError() != EGL_SUCCESS){} EGLImage image = self->params.egl->eglCreateImage(self->params.egl->egl_display, 0, EGL_LINUX_DMA_BUF_EXT, NULL, img_attr); diff --git a/src/pipewire.c b/src/pipewire.c index fedd301..db3122a 100644 --- a/src/pipewire.c +++ b/src/pipewire.c @@ -1,5 +1,6 @@ #include "../include/pipewire.h" #include "../include/egl.h" +#include "../include/utils.h" #include #include @@ -112,17 +113,21 @@ static void on_process_cb(void *user_data) { pthread_mutex_lock(&self->mutex); if(buffer->datas[0].type == SPA_DATA_DmaBuf) { - if(self->dmabuf_data.fd > 0) { - close(self->dmabuf_data.fd); - self->dmabuf_data.fd = -1; + for(size_t i = 0; i < self->dmabuf_num_planes; ++i) { + if(self->dmabuf_data[i].fd > 0) { + close(self->dmabuf_data[i].fd); + self->dmabuf_data[i].fd = -1; + } } - if(buffer->n_datas > 0) { - self->dmabuf_data.fd = dup(buffer->datas[0].fd); - self->dmabuf_data.offset = buffer->datas[0].chunk->offset; - self->dmabuf_data.stride = buffer->datas[0].chunk->stride; - } else { - self->dmabuf_data.fd = -1; + self->dmabuf_num_planes = buffer->n_datas; + if(self->dmabuf_num_planes > GSR_PIPEWIRE_DMABUF_MAX_PLANES) + self->dmabuf_num_planes = GSR_PIPEWIRE_DMABUF_MAX_PLANES; + + for(size_t i = 0; i < self->dmabuf_num_planes; ++i) { + self->dmabuf_data[i].fd = dup(buffer->datas[i].fd); + self->dmabuf_data[i].offset = buffer->datas[i].chunk->offset; + self->dmabuf_data[i].stride = buffer->datas[i].chunk->stride; } } else { // TODO: @@ -214,7 +219,7 @@ static void on_param_changed_cb(void *user_data, uint32_t id, const struct spa_p spa_debug_type_find_name(spa_type_video_format, self->format.info.raw.format)); if(has_modifier) { - fprintf(stderr, "gsr info: pipewire: Modifier: %" PRIu64 "\n", self->format.info.raw.modifier); + fprintf(stderr, "gsr info: pipewire: Modifier: 0x%" PRIx64 "\n", self->format.info.raw.modifier); } fprintf(stderr, "gsr info: pipewire: Size: %dx%d\n", self->format.info.raw.size.width, self->format.info.raw.size.height); @@ -319,12 +324,8 @@ static int64_t spa_video_format_to_drm_format(const enum spa_video_format format switch(format) { case SPA_VIDEO_FORMAT_RGBx: return DRM_FORMAT_XBGR8888; case SPA_VIDEO_FORMAT_BGRx: return DRM_FORMAT_XRGB8888; - case SPA_VIDEO_FORMAT_xRGB: return DRM_FORMAT_BGRX8888; - case SPA_VIDEO_FORMAT_xBGR: return DRM_FORMAT_RGBX8888; case SPA_VIDEO_FORMAT_RGBA: return DRM_FORMAT_ABGR8888; case SPA_VIDEO_FORMAT_BGRA: return DRM_FORMAT_ARGB8888; - case SPA_VIDEO_FORMAT_ARGB: return DRM_FORMAT_BGRA8888; - case SPA_VIDEO_FORMAT_ABGR: return DRM_FORMAT_RGBA8888; case SPA_VIDEO_FORMAT_RGB: return DRM_FORMAT_XBGR8888; case SPA_VIDEO_FORMAT_BGR: return DRM_FORMAT_XRGB8888; default: break; @@ -335,13 +336,9 @@ static int64_t spa_video_format_to_drm_format(const enum spa_video_format format static const enum spa_video_format video_formats[] = { SPA_VIDEO_FORMAT_BGRA, SPA_VIDEO_FORMAT_BGRx, - SPA_VIDEO_FORMAT_ABGR, - SPA_VIDEO_FORMAT_xBGR, SPA_VIDEO_FORMAT_BGR, SPA_VIDEO_FORMAT_RGBx, - SPA_VIDEO_FORMAT_xRGB, SPA_VIDEO_FORMAT_RGBA, - SPA_VIDEO_FORMAT_ARGB, SPA_VIDEO_FORMAT_RGB, }; @@ -401,7 +398,7 @@ static bool spa_video_format_get_modifiers(gsr_pipewire *self, const enum spa_vi const int64_t drm_format = spa_video_format_to_drm_format(format); if(!self->egl->eglQueryDmaBufModifiersEXT(self->egl->egl_display, drm_format, max_modifiers, modifiers, NULL, num_modifiers)) { - fprintf(stderr, "gsr error: spa_video_format_get_modifiers: eglQueryDmaBufModifiersEXT failed with drm format %" PRIi64 "\n", drm_format); + fprintf(stderr, "gsr error: spa_video_format_get_modifiers: eglQueryDmaBufModifiersEXT failed with drm format %d, %" PRIi64 "\n", (int)format, drm_format); //modifiers[0] = DRM_FORMAT_MOD_LINEAR; //modifiers[1] = DRM_FORMAT_MOD_INVALID; //*num_modifiers = 2; @@ -595,10 +592,13 @@ void gsr_pipewire_deinit(gsr_pipewire *self) { self->fd = -1; } - if(self->dmabuf_data.fd > 0) { - close(self->dmabuf_data.fd); - self->dmabuf_data.fd = -1; + for(size_t i = 0; i < self->dmabuf_num_planes; ++i) { + if(self->dmabuf_data[i].fd > 0) { + close(self->dmabuf_data[i].fd); + self->dmabuf_data[i].fd = -1; + } } + self->dmabuf_num_planes = 0; self->negotiated = false; @@ -620,30 +620,32 @@ void gsr_pipewire_deinit(gsr_pipewire *self) { } } -bool gsr_pipewire_map_texture(gsr_pipewire *self, unsigned int texture_id, unsigned int cursor_texture_id, gsr_pipewire_region *region, gsr_pipewire_region *cursor_region, int *plane_fd) { - *plane_fd = -1; +bool gsr_pipewire_map_texture(gsr_pipewire *self, unsigned int texture_id, unsigned int cursor_texture_id, gsr_pipewire_region *region, gsr_pipewire_region *cursor_region, int *plane_fds, int *num_plane_fds) { + for(int i = 0; i < GSR_PIPEWIRE_DMABUF_MAX_PLANES; ++i) { + plane_fds[i] = -1; + } + *num_plane_fds = 0; pthread_mutex_lock(&self->mutex); - if(!self->negotiated || self->dmabuf_data.fd <= 0) { + if(!self->negotiated || self->dmabuf_data[0].fd <= 0) { pthread_mutex_unlock(&self->mutex); return false; } - *plane_fd = self->dmabuf_data.fd; - self->dmabuf_data.fd = -1; + int fds[GSR_PIPEWIRE_DMABUF_MAX_PLANES]; + uint32_t offsets[GSR_PIPEWIRE_DMABUF_MAX_PLANES]; + uint32_t pitches[GSR_PIPEWIRE_DMABUF_MAX_PLANES]; + uint64_t modifiers[GSR_PIPEWIRE_DMABUF_MAX_PLANES]; + for(size_t i = 0; i < self->dmabuf_num_planes; ++i) { + fds[i] = self->dmabuf_data[i].fd; + offsets[i] = self->dmabuf_data[i].offset; + pitches[i] = self->dmabuf_data[i].stride; + modifiers[i] = self->format.info.raw.modifier; + } - /* TODO: Support multiple planes */ - const intptr_t img_attr[] = { - EGL_LINUX_DRM_FOURCC_EXT, spa_video_format_to_drm_format(self->format.info.raw.format), - EGL_WIDTH, self->format.info.raw.size.width, - EGL_HEIGHT, self->format.info.raw.size.height, - EGL_DMA_BUF_PLANE0_FD_EXT, *plane_fd, - EGL_DMA_BUF_PLANE0_OFFSET_EXT, self->dmabuf_data.offset, - EGL_DMA_BUF_PLANE0_PITCH_EXT, self->dmabuf_data.stride, - EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, self->format.info.raw.modifier & 0xFFFFFFFFULL, - EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, self->format.info.raw.modifier >> 32ULL, - EGL_NONE - }; + intptr_t img_attr[44]; + setup_dma_buf_attrs(img_attr, spa_video_format_to_drm_format(self->format.info.raw.format), self->format.info.raw.size.width, self->format.info.raw.size.height, + fds, offsets, pitches, modifiers, self->dmabuf_num_planes, true); EGLImage image = self->egl->eglCreateImage(self->egl->egl_display, 0, EGL_LINUX_DMA_BUF_EXT, NULL, img_attr); self->egl->glBindTexture(GL_TEXTURE_2D, texture_id); @@ -686,6 +688,13 @@ bool gsr_pipewire_map_texture(gsr_pipewire *self, unsigned int texture_id, unsig cursor_region->width = self->cursor.width; cursor_region->height = self->cursor.height; + for(size_t i = 0; i < self->dmabuf_num_planes; ++i) { + plane_fds[i] = self->dmabuf_data[i].fd; + self->dmabuf_data[i].fd = -1; + } + *num_plane_fds = self->dmabuf_num_planes; + self->dmabuf_num_planes = 0; + pthread_mutex_unlock(&self->mutex); return true; } diff --git a/src/utils.c b/src/utils.c index d768f58..28c66e0 100644 --- a/src/utils.c +++ b/src/utils.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -518,3 +519,95 @@ int create_directory_recursive(char *path) { } return 0; } + +void setup_dma_buf_attrs(intptr_t *img_attr, uint32_t format, uint32_t width, uint32_t height, const int *fds, const uint32_t *offsets, const uint32_t *pitches, const uint64_t *modifiers, int num_planes, bool use_modifier) { + size_t img_attr_index = 0; + + img_attr[img_attr_index++] = EGL_LINUX_DRM_FOURCC_EXT; + img_attr[img_attr_index++] = format; + + img_attr[img_attr_index++] = EGL_WIDTH; + img_attr[img_attr_index++] = width; + + img_attr[img_attr_index++] = EGL_HEIGHT; + img_attr[img_attr_index++] = height; + + if(num_planes >= 1) { + img_attr[img_attr_index++] = EGL_DMA_BUF_PLANE0_FD_EXT; + img_attr[img_attr_index++] = fds[0]; + + img_attr[img_attr_index++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT; + img_attr[img_attr_index++] = offsets[0]; + + img_attr[img_attr_index++] = EGL_DMA_BUF_PLANE0_PITCH_EXT; + img_attr[img_attr_index++] = pitches[0]; + + if(use_modifier) { + img_attr[img_attr_index++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT; + img_attr[img_attr_index++] = modifiers[0] & 0xFFFFFFFFULL; + + img_attr[img_attr_index++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT; + img_attr[img_attr_index++] = modifiers[0] >> 32ULL; + } + } + + if(num_planes >= 2) { + img_attr[img_attr_index++] = EGL_DMA_BUF_PLANE1_FD_EXT; + img_attr[img_attr_index++] = fds[1]; + + img_attr[img_attr_index++] = EGL_DMA_BUF_PLANE1_OFFSET_EXT; + img_attr[img_attr_index++] = offsets[1]; + + img_attr[img_attr_index++] = EGL_DMA_BUF_PLANE1_PITCH_EXT; + img_attr[img_attr_index++] = pitches[1]; + + if(use_modifier) { + img_attr[img_attr_index++] = EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT; + img_attr[img_attr_index++] = modifiers[1] & 0xFFFFFFFFULL; + + img_attr[img_attr_index++] = EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT; + img_attr[img_attr_index++] = modifiers[1] >> 32ULL; + } + } + + if(num_planes >= 3) { + img_attr[img_attr_index++] = EGL_DMA_BUF_PLANE2_FD_EXT; + img_attr[img_attr_index++] = fds[2]; + + img_attr[img_attr_index++] = EGL_DMA_BUF_PLANE2_OFFSET_EXT; + img_attr[img_attr_index++] = offsets[2]; + + img_attr[img_attr_index++] = EGL_DMA_BUF_PLANE2_PITCH_EXT; + img_attr[img_attr_index++] = pitches[2]; + + if(use_modifier) { + img_attr[img_attr_index++] = EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT; + img_attr[img_attr_index++] = modifiers[2] & 0xFFFFFFFFULL; + + img_attr[img_attr_index++] = EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT; + img_attr[img_attr_index++] = modifiers[2] >> 32ULL; + } + } + + if(num_planes >= 4) { + img_attr[img_attr_index++] = EGL_DMA_BUF_PLANE3_FD_EXT; + img_attr[img_attr_index++] = fds[3]; + + img_attr[img_attr_index++] = EGL_DMA_BUF_PLANE3_OFFSET_EXT; + img_attr[img_attr_index++] = offsets[3]; + + img_attr[img_attr_index++] = EGL_DMA_BUF_PLANE3_PITCH_EXT; + img_attr[img_attr_index++] = pitches[3]; + + if(use_modifier) { + img_attr[img_attr_index++] = EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT; + img_attr[img_attr_index++] = modifiers[3] & 0xFFFFFFFFULL; + + img_attr[img_attr_index++] = EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT; + img_attr[img_attr_index++] = modifiers[3] >> 32ULL; + } + } + + img_attr[img_attr_index++] = EGL_NONE; + assert(img_attr_index <= 44); +} -- cgit v1.2.3