From ffb8c6af3bc6614665f1c5102cdab779963e39c1 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Thu, 15 Aug 2024 08:09:23 +0200 Subject: Allow prime-run capture on laptop with external gpu when the iGPU doesn't have any monitor to capture. Fix prime-run desktop portal capture broken (fallback to external texture). Fallback to external texture in kms capture. --- TODO | 4 ++- include/pipewire.h | 11 +++++-- src/capture/kms.c | 93 ++++++++++++++++++++++++++++++++++------------------ src/capture/portal.c | 58 +++++++++++++++++++------------- src/main.cpp | 8 ++--- src/pipewire.c | 68 +++++++++++++++++++++++++++----------- 6 files changed, 162 insertions(+), 80 deletions(-) diff --git a/TODO b/TODO index 22747cd..7ecc530 100644 --- a/TODO +++ b/TODO @@ -154,4 +154,6 @@ Add the option to specify a path to desktop portal session token, to allow diffe Explicit sync is done with the drm property IN_FENCE_FD (see https://drmdb.emersion.fr/properties/4008636142/IN_FENCE_FD). Check if this needs to be used on wayland (especially on nvidia) when capturing a monitor directly without desktop portal. -The update fps appear to be lower when recording a monitor instead of using portal on intel. Does this reflect in game framerate? \ No newline at end of file +The update fps appear to be lower when recording a monitor instead of using portal on intel. Does this reflect in game framerate? + +Recording a monitor belonging to another gpu might be possible by using GL_TEXTURE_EXTERNAL_OES instead for the texture. \ No newline at end of file diff --git a/include/pipewire.h b/include/pipewire.h index 4a6330d..d4abe42 100644 --- a/include/pipewire.h +++ b/include/pipewire.h @@ -42,6 +42,12 @@ typedef struct { size_t modifiers_size; } gsr_video_format; +typedef struct { + unsigned int texture_id; + unsigned int external_texture_id; + unsigned int cursor_texture_id; +} gsr_texture_map; + typedef struct { gsr_egl *egl; int fd; @@ -85,6 +91,7 @@ typedef struct { bool started; bool stopped; bool no_modifiers_fallback; + bool external_texture_fallback; uint64_t modifiers[GSR_PIPEWIRE_MAX_MODIFIERS]; size_t num_modifiers; @@ -98,8 +105,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); -/* |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); +/* |plane_fds| should be at least GSR_PIPEWIRE_DMABUF_MAX_PLANES in size */ +bool gsr_pipewire_map_texture(gsr_pipewire *self, gsr_texture_map texture_map, gsr_pipewire_region *region, gsr_pipewire_region *cursor_region, int *plane_fds, int *num_plane_fds, bool *using_external_image); bool gsr_pipewire_recording_stopped(gsr_pipewire *self); #endif /* GSR_PIPEWIRE_H */ diff --git a/src/capture/kms.c b/src/capture/kms.c index 2a864ee..4e2a24e 100644 --- a/src/capture/kms.c +++ b/src/capture/kms.c @@ -37,9 +37,11 @@ typedef struct { gsr_monitor_rotation monitor_rotation; unsigned int input_texture_id; + unsigned int external_input_texture_id; unsigned int cursor_texture_id; bool no_modifiers_fallback; + bool external_texture_fallback; struct hdr_output_metadata hdr_metadata; bool hdr_metadata_set; @@ -65,6 +67,11 @@ static void gsr_capture_kms_stop(gsr_capture_kms *self) { self->input_texture_id = 0; } + if(self->external_input_texture_id) { + self->params.egl->glDeleteTextures(1, &self->external_input_texture_id); + self->external_input_texture_id = 0; + } + if(self->cursor_texture_id) { self->params.egl->glDeleteTextures(1, &self->cursor_texture_id); self->cursor_texture_id = 0; @@ -80,12 +87,20 @@ static int max_int(int a, int b) { static void gsr_capture_kms_create_input_texture_ids(gsr_capture_kms *self) { self->params.egl->glGenTextures(1, &self->input_texture_id); - self->params.egl->glBindTexture(GL_TEXTURE_EXTERNAL_OES, self->input_texture_id); - self->params.egl->glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - self->params.egl->glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - self->params.egl->glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - self->params.egl->glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - self->params.egl->glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0); + self->params.egl->glBindTexture(GL_TEXTURE_2D, self->input_texture_id); + self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + self->params.egl->glBindTexture(GL_TEXTURE_2D, 0); + + self->params.egl->glGenTextures(1, &self->external_input_texture_id); + self->params.egl->glBindTexture(GL_TEXTURE_2D, self->external_input_texture_id); + self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + self->params.egl->glBindTexture(GL_TEXTURE_2D, 0); const bool cursor_texture_id_is_external = self->params.egl->gpu_info.vendor == GSR_GPU_VENDOR_NVIDIA; const int cursor_texture_id_target = cursor_texture_id_is_external ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D; @@ -253,6 +268,30 @@ static vec2i swap_vec2i(vec2i value) { return value; } +static EGLImage gsr_capture_kms_create_egl_image(gsr_capture_kms *self, gsr_kms_response_item *drm_fd, const int *fds, const uint32_t *offsets, const uint32_t *pitches, const uint64_t *modifiers, bool use_modifiers) { + 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, use_modifiers); + 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); + if(!image || self->params.egl->eglGetError() != EGL_SUCCESS) { + if(image) + self->params.egl->eglDestroyImage(self->params.egl->egl_display, image); + return NULL; + } + return image; +} + +static bool gsr_capture_kms_bind_image_to_texture(gsr_capture_kms *self, EGLImage image, unsigned int texture_id, bool external_texture) { + const int texture_target = external_texture ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D; + while(self->params.egl->glGetError() != 0){} + self->params.egl->glBindTexture(texture_target, texture_id); + self->params.egl->glEGLImageTargetTexture2DOES(texture_target, image); + const bool success = self->params.egl->glGetError() == 0; + self->params.egl->eglDestroyImage(self->params.egl->egl_display, image); + self->params.egl->glBindTexture(texture_target, 0); + return success; +} + static int gsr_capture_kms_capture(gsr_capture *cap, AVFrame *frame, gsr_color_conversion *color_conversion) { gsr_capture_kms *self = cap->priv; const bool cursor_texture_id_is_external = self->params.egl->gpu_info.vendor == GSR_GPU_VENDOR_NVIDIA; @@ -334,36 +373,26 @@ static int gsr_capture_kms_capture(gsr_capture *cap, AVFrame *frame, gsr_color_c } EGLImage image = NULL; - intptr_t img_attr[44]; - if(self->no_modifiers_fallback) { - 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, false); - image = self->params.egl->eglCreateImage(self->params.egl->egl_display, 0, EGL_LINUX_DMA_BUF_EXT, NULL, img_attr); + image = gsr_capture_kms_create_egl_image(self, drm_fd, fds, offsets, pitches, modifiers, false); } else { - 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, true); - while(self->params.egl->eglGetError() != EGL_SUCCESS){} - image = self->params.egl->eglCreateImage(self->params.egl->egl_display, 0, EGL_LINUX_DMA_BUF_EXT, NULL, img_attr); - if(!image || self->params.egl->eglGetError() != EGL_SUCCESS) { + image = gsr_capture_kms_create_egl_image(self, drm_fd, fds, offsets, pitches, modifiers, true); + if(!image) { fprintf(stderr, "gsr error: gsr_capture_kms_capture: failed to create egl image with modifiers, trying without modifiers\n"); self->no_modifiers_fallback = true; - 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, false); - image = self->params.egl->eglCreateImage(self->params.egl->egl_display, 0, EGL_LINUX_DMA_BUF_EXT, NULL, img_attr); + image = gsr_capture_kms_create_egl_image(self, drm_fd, fds, offsets, pitches, modifiers, false); } } - while(self->params.egl->glGetError() != 0){} - if(self->params.egl->glGetError() != 0) - fprintf(stderr, "kms error 1\n"); - self->params.egl->glBindTexture(GL_TEXTURE_EXTERNAL_OES, self->input_texture_id); - if(self->params.egl->glGetError() != 0) - fprintf(stderr, "kms error 2\n"); - self->params.egl->glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, image); - if(self->params.egl->glGetError() != 0) - fprintf(stderr, "kms error 3\n"); - self->params.egl->eglDestroyImage(self->params.egl->egl_display, image); - if(self->params.egl->glGetError() != 0) - fprintf(stderr, "kms error 4\n"); - self->params.egl->glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0); + if(self->external_texture_fallback) { + gsr_capture_kms_bind_image_to_texture(self, image, self->external_input_texture_id, true); + } else { + if(!gsr_capture_kms_bind_image_to_texture(self, image, self->input_texture_id, false)) { + fprintf(stderr, "gsr error: gsr_pipewire_map_texture: failed to bind image to texture, trying with external texture\n"); + self->external_texture_fallback = true; + gsr_capture_kms_bind_image_to_texture(self, image, self->external_input_texture_id, true); + } + } vec2i capture_pos = self->capture_pos; if(!capture_is_combined_plane) @@ -374,10 +403,10 @@ static int gsr_capture_kms_capture(gsr_capture *cap, AVFrame *frame, gsr_color_c const int target_x = max_int(0, frame->width / 2 - self->capture_size.x / 2); const int target_y = max_int(0, frame->height / 2 - self->capture_size.y / 2); - gsr_color_conversion_draw(color_conversion, self->input_texture_id, + gsr_color_conversion_draw(color_conversion, self->external_texture_fallback ? self->external_input_texture_id : self->input_texture_id, (vec2i){target_x, target_y}, self->capture_size, capture_pos, self->capture_size, - texture_rotation, true); + texture_rotation, self->external_texture_fallback); if(self->params.record_cursor && cursor_drm_fd) { const vec2i cursor_size = {cursor_drm_fd->width, cursor_drm_fd->height}; @@ -462,7 +491,7 @@ static gsr_source_color gsr_capture_kms_get_source_color(gsr_capture *cap) { } static bool gsr_capture_kms_uses_external_image(gsr_capture *cap) { - gsr_capture_kms *self = cap->priv; + (void)cap; return true; } diff --git a/src/capture/portal.c b/src/capture/portal.c index fa6f61f..2e34101 100644 --- a/src/capture/portal.c +++ b/src/capture/portal.c @@ -15,8 +15,7 @@ typedef struct { gsr_capture_portal_params params; - unsigned int input_texture_id; - unsigned int cursor_texture_id; + gsr_texture_map texture_map; gsr_dbus dbus; char *session_handle; @@ -38,14 +37,19 @@ static void gsr_capture_portal_cleanup_plane_fds(gsr_capture_portal *self) { } static void gsr_capture_portal_stop(gsr_capture_portal *self) { - if(self->input_texture_id) { - self->params.egl->glDeleteTextures(1, &self->input_texture_id); - self->input_texture_id = 0; + if(self->texture_map.texture_id) { + self->params.egl->glDeleteTextures(1, &self->texture_map.texture_id); + self->texture_map.texture_id = 0; } - if(self->cursor_texture_id) { - self->params.egl->glDeleteTextures(1, &self->cursor_texture_id); - self->cursor_texture_id = 0; + if(self->texture_map.external_texture_id) { + self->params.egl->glDeleteTextures(1, &self->texture_map.external_texture_id); + self->texture_map.external_texture_id = 0; + } + + if(self->texture_map.cursor_texture_id) { + self->params.egl->glDeleteTextures(1, &self->texture_map.cursor_texture_id); + self->texture_map.cursor_texture_id = 0; } gsr_capture_portal_cleanup_plane_fds(self); @@ -61,16 +65,24 @@ static void gsr_capture_portal_stop(gsr_capture_portal *self) { } static void gsr_capture_portal_create_input_textures(gsr_capture_portal *self) { - self->params.egl->glGenTextures(1, &self->input_texture_id); - self->params.egl->glBindTexture(GL_TEXTURE_2D, self->input_texture_id); + self->params.egl->glGenTextures(1, &self->texture_map.texture_id); + self->params.egl->glBindTexture(GL_TEXTURE_2D, self->texture_map.texture_id); self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); self->params.egl->glBindTexture(GL_TEXTURE_2D, 0); - self->params.egl->glGenTextures(1, &self->cursor_texture_id); - self->params.egl->glBindTexture(GL_TEXTURE_2D, self->cursor_texture_id); + self->params.egl->glGenTextures(1, &self->texture_map.external_texture_id); + self->params.egl->glBindTexture(GL_TEXTURE_EXTERNAL_OES, self->texture_map.external_texture_id); + self->params.egl->glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + self->params.egl->glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + self->params.egl->glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + self->params.egl->glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + self->params.egl->glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0); + + self->params.egl->glGenTextures(1, &self->texture_map.cursor_texture_id); + self->params.egl->glBindTexture(GL_TEXTURE_2D, self->texture_map.cursor_texture_id); self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); @@ -224,7 +236,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) { - 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)) { + bool uses_external_image = false; + if(gsr_pipewire_map_texture(&self->pipewire, self->texture_map, ®ion, &cursor_region, self->plane_fds, &self->num_plane_fds, &uses_external_image)) { gsr_capture_portal_cleanup_plane_fds(self); self->capture_size.x = region.width; self->capture_size.y = region.height; @@ -304,7 +317,8 @@ static int gsr_capture_portal_capture(gsr_capture *cap, AVFrame *frame, gsr_colo /* 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_fds, &self->num_plane_fds)) { + bool using_external_image = false; + if(gsr_pipewire_map_texture(&self->pipewire, self->texture_map, ®ion, &cursor_region, self->plane_fds, &self->num_plane_fds, &using_external_image)) { 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; @@ -315,10 +329,10 @@ static int gsr_capture_portal_capture(gsr_capture *cap, AVFrame *frame, gsr_colo const int target_x = max_int(0, frame->width / 2 - self->capture_size.x / 2); const int target_y = max_int(0, frame->height / 2 - self->capture_size.y / 2); - gsr_color_conversion_draw(color_conversion, self->input_texture_id, + gsr_color_conversion_draw(color_conversion, using_external_image ? self->texture_map.external_texture_id : self->texture_map.texture_id, (vec2i){target_x, target_y}, self->capture_size, (vec2i){region.x, region.y}, self->capture_size, - 0.0f, false); + 0.0f, using_external_image); if(self->params.record_cursor) { const vec2i cursor_pos = { @@ -328,7 +342,7 @@ static int gsr_capture_portal_capture(gsr_capture *cap, AVFrame *frame, gsr_colo self->params.egl->glEnable(GL_SCISSOR_TEST); self->params.egl->glScissor(target_x, target_y, self->capture_size.x, self->capture_size.y); - gsr_color_conversion_draw(color_conversion, self->cursor_texture_id, + gsr_color_conversion_draw(color_conversion, self->texture_map.cursor_texture_id, (vec2i){cursor_pos.x, cursor_pos.y}, (vec2i){cursor_region.width, cursor_region.height}, (vec2i){0, 0}, (vec2i){cursor_region.width, cursor_region.height}, 0.0f, false); @@ -359,10 +373,10 @@ static gsr_source_color gsr_capture_portal_get_source_color(gsr_capture *cap) { return GSR_SOURCE_COLOR_RGB; } -// static bool gsr_capture_portal_uses_external_image(gsr_capture *cap) { -// gsr_capture_portal *cap_portal = cap->priv; -// return cap_portal->params.egl->gpu_info.vendor == GSR_GPU_VENDOR_NVIDIA; -// } +static bool gsr_capture_portal_uses_external_image(gsr_capture *cap) { + (void)cap; + return true; +} static void gsr_capture_portal_destroy(gsr_capture *cap, AVCodecContext *video_codec_context) { (void)video_codec_context; @@ -400,7 +414,7 @@ gsr_capture* gsr_capture_portal_create(const gsr_capture_portal_params *params) .capture = gsr_capture_portal_capture, .capture_end = gsr_capture_portal_capture_end, .get_source_color = gsr_capture_portal_get_source_color, - .uses_external_image = NULL, + .uses_external_image = gsr_capture_portal_uses_external_image, .destroy = gsr_capture_portal_destroy, .priv = cap_portal }; diff --git a/src/main.cpp b/src/main.cpp index b4bb46b..8c09c7b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1782,8 +1782,8 @@ static void info_command() { egl.card_path[0] = '\0'; if(monitor_capture_use_drm(&egl, wayland)) { // TODO: Allow specifying another card, and in other places - if(!gsr_get_valid_card_path(&egl, egl.card_path, true)) { - fprintf(stderr, "Error: no /dev/dri/cardX device found. Make sure that you have at least one monitor connected or record a single window instead on X11\n"); + if(!gsr_get_valid_card_path(&egl, egl.card_path, false)) { + fprintf(stderr, "Error: no /dev/dri/cardX device found. Make sure that you have at least one monitor connected\n"); _exit(23); } } @@ -2470,7 +2470,7 @@ int main(int argc, char **argv) { _exit(1); } - const bool is_monitor_capture = strcmp(window_str, "focused") != 0 && contains_non_hex_number(window_str); + const bool is_monitor_capture = strcmp(window_str, "focused") != 0 && strcmp(window_str, "portal") != 0 && contains_non_hex_number(window_str); gsr_egl egl; if(!gsr_egl_load(&egl, dpy, wayland, is_monitor_capture)) { fprintf(stderr, "gsr error: failed to load opengl\n"); @@ -2498,7 +2498,7 @@ int main(int argc, char **argv) { if(monitor_capture_use_drm(&egl, wayland)) { // TODO: Allow specifying another card, and in other places if(!gsr_get_valid_card_path(&egl, egl.card_path, is_monitor_capture)) { - fprintf(stderr, "Error: no /dev/dri/cardX device found. Make sure that you have at least one monitor connected or record a single window instead on X11\n"); + fprintf(stderr, "Error: no /dev/dri/cardX device found. Make sure that you have at least one monitor connected or record a single window instead on X11 or record with the -w portal option\n"); _exit(2); } } diff --git a/src/pipewire.c b/src/pipewire.c index 2e73b92..35f0e45 100644 --- a/src/pipewire.c +++ b/src/pipewire.c @@ -620,11 +620,37 @@ 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_fds, int *num_plane_fds) { +static EGLImage gsr_pipewire_create_egl_image(gsr_pipewire *self, const int *fds, const uint32_t *offsets, const uint32_t *pitches, const uint64_t *modifiers, bool use_modifiers) { + 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, use_modifiers); + while(self->egl->eglGetError() != EGL_SUCCESS){} + EGLImage image = self->egl->eglCreateImage(self->egl->egl_display, 0, EGL_LINUX_DMA_BUF_EXT, NULL, img_attr); + if(!image || self->egl->eglGetError() != EGL_SUCCESS) { + if(image) + self->egl->eglDestroyImage(self->egl->egl_display, image); + return NULL; + } + return image; +} + +static bool gsr_pipewire_bind_image_to_texture(gsr_pipewire *self, EGLImage image, unsigned int texture_id, bool external_texture) { + const int texture_target = external_texture ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D; + while(self->egl->glGetError() != 0){} + self->egl->glBindTexture(texture_target, texture_id); + self->egl->glEGLImageTargetTexture2DOES(texture_target, image); + const bool success = self->egl->glGetError() == 0; + self->egl->eglDestroyImage(self->egl->egl_display, image); + self->egl->glBindTexture(texture_target, 0); + return success; +} + +bool gsr_pipewire_map_texture(gsr_pipewire *self, gsr_texture_map texture_map, gsr_pipewire_region *region, gsr_pipewire_region *cursor_region, int *plane_fds, int *num_plane_fds, bool *using_external_image) { for(int i = 0; i < GSR_PIPEWIRE_DMABUF_MAX_PLANES; ++i) { plane_fds[i] = -1; } *num_plane_fds = 0; + *using_external_image = false; pthread_mutex_lock(&self->mutex); if(!self->negotiated || self->dmabuf_data[0].fd <= 0) { @@ -644,34 +670,38 @@ bool gsr_pipewire_map_texture(gsr_pipewire *self, unsigned int texture_id, unsig } EGLImage image = NULL; - intptr_t img_attr[44]; - if(self->no_modifiers_fallback) { - 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, false); - image = self->egl->eglCreateImage(self->egl->egl_display, 0, EGL_LINUX_DMA_BUF_EXT, NULL, img_attr); + image = gsr_pipewire_create_egl_image(self, fds, offsets, pitches, modifiers, false); } else { - 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); - - while(self->egl->eglGetError() != EGL_SUCCESS){} - image = self->egl->eglCreateImage(self->egl->egl_display, 0, EGL_LINUX_DMA_BUF_EXT, NULL, img_attr); - if(!image || self->egl->eglGetError() != EGL_SUCCESS) { + image = gsr_pipewire_create_egl_image(self, fds, offsets, pitches, modifiers, true); + if(!image) { fprintf(stderr, "gsr error: gsr_pipewire_map_texture: failed to create egl image with modifiers, trying without modifiers\n"); self->no_modifiers_fallback = true; - 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, false); - image = self->egl->eglCreateImage(self->egl->egl_display, 0, EGL_LINUX_DMA_BUF_EXT, NULL, img_attr); + image = gsr_pipewire_create_egl_image(self, fds, offsets, pitches, modifiers, false); + } + } + + if(self->external_texture_fallback) { + gsr_pipewire_bind_image_to_texture(self, image, texture_map.external_texture_id, true); + *using_external_image = true; + } else { + if(!gsr_pipewire_bind_image_to_texture(self, image, texture_map.texture_id, false)) { + fprintf(stderr, "gsr error: gsr_pipewire_map_texture: failed to bind image to texture, trying with external texture\n"); + self->external_texture_fallback = true; + gsr_pipewire_bind_image_to_texture(self, image, texture_map.external_texture_id, true); + *using_external_image = true; } } - self->egl->glBindTexture(GL_TEXTURE_2D, texture_id); - self->egl->glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image); + const int texture_target = self->external_texture_fallback ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D; + while(self->egl->glGetError() != 0){} + self->egl->glBindTexture(texture_target, texture_map.texture_id); + self->egl->glEGLImageTargetTexture2DOES(texture_target, image); self->egl->eglDestroyImage(self->egl->egl_display, image); - self->egl->glBindTexture(GL_TEXTURE_2D, 0); + self->egl->glBindTexture(texture_target, 0); if(self->cursor.data) { - self->egl->glBindTexture(GL_TEXTURE_2D, cursor_texture_id); + self->egl->glBindTexture(GL_TEXTURE_2D, texture_map.cursor_texture_id); // TODO: glTextureSubImage2D if same size self->egl->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, self->cursor.width, self->cursor.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, self->cursor.data); self->egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); -- cgit v1.2.3