mirror of
https://repo.dec05eba.com/gpu-screen-recorder
synced 2026-05-07 15:19:55 +09:00
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.
This commit is contained in:
2
TODO
2
TODO
@@ -155,3 +155,5 @@ 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.
|
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?
|
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.
|
||||||
@@ -42,6 +42,12 @@ typedef struct {
|
|||||||
size_t modifiers_size;
|
size_t modifiers_size;
|
||||||
} gsr_video_format;
|
} gsr_video_format;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
unsigned int texture_id;
|
||||||
|
unsigned int external_texture_id;
|
||||||
|
unsigned int cursor_texture_id;
|
||||||
|
} gsr_texture_map;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
gsr_egl *egl;
|
gsr_egl *egl;
|
||||||
int fd;
|
int fd;
|
||||||
@@ -85,6 +91,7 @@ typedef struct {
|
|||||||
bool started;
|
bool started;
|
||||||
bool stopped;
|
bool stopped;
|
||||||
bool no_modifiers_fallback;
|
bool no_modifiers_fallback;
|
||||||
|
bool external_texture_fallback;
|
||||||
|
|
||||||
uint64_t modifiers[GSR_PIPEWIRE_MAX_MODIFIERS];
|
uint64_t modifiers[GSR_PIPEWIRE_MAX_MODIFIERS];
|
||||||
size_t num_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);
|
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);
|
void gsr_pipewire_deinit(gsr_pipewire *self);
|
||||||
|
|
||||||
/* |plane_fds| should be at GSR_PIPEWIRE_DMABUF_MAX_PLANES in size */
|
/* |plane_fds| should be at least 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_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);
|
bool gsr_pipewire_recording_stopped(gsr_pipewire *self);
|
||||||
|
|
||||||
#endif /* GSR_PIPEWIRE_H */
|
#endif /* GSR_PIPEWIRE_H */
|
||||||
|
|||||||
@@ -37,9 +37,11 @@ typedef struct {
|
|||||||
gsr_monitor_rotation monitor_rotation;
|
gsr_monitor_rotation monitor_rotation;
|
||||||
|
|
||||||
unsigned int input_texture_id;
|
unsigned int input_texture_id;
|
||||||
|
unsigned int external_input_texture_id;
|
||||||
unsigned int cursor_texture_id;
|
unsigned int cursor_texture_id;
|
||||||
|
|
||||||
bool no_modifiers_fallback;
|
bool no_modifiers_fallback;
|
||||||
|
bool external_texture_fallback;
|
||||||
|
|
||||||
struct hdr_output_metadata hdr_metadata;
|
struct hdr_output_metadata hdr_metadata;
|
||||||
bool hdr_metadata_set;
|
bool hdr_metadata_set;
|
||||||
@@ -65,6 +67,11 @@ static void gsr_capture_kms_stop(gsr_capture_kms *self) {
|
|||||||
self->input_texture_id = 0;
|
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) {
|
if(self->cursor_texture_id) {
|
||||||
self->params.egl->glDeleteTextures(1, &self->cursor_texture_id);
|
self->params.egl->glDeleteTextures(1, &self->cursor_texture_id);
|
||||||
self->cursor_texture_id = 0;
|
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) {
|
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->glGenTextures(1, &self->input_texture_id);
|
||||||
self->params.egl->glBindTexture(GL_TEXTURE_EXTERNAL_OES, self->input_texture_id);
|
self->params.egl->glBindTexture(GL_TEXTURE_2D, 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_2D, 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_2D, 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_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
self->params.egl->glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
self->params.egl->glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0);
|
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 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;
|
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;
|
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) {
|
static int gsr_capture_kms_capture(gsr_capture *cap, AVFrame *frame, gsr_color_conversion *color_conversion) {
|
||||||
gsr_capture_kms *self = cap->priv;
|
gsr_capture_kms *self = cap->priv;
|
||||||
const bool cursor_texture_id_is_external = self->params.egl->gpu_info.vendor == GSR_GPU_VENDOR_NVIDIA;
|
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;
|
EGLImage image = NULL;
|
||||||
intptr_t img_attr[44];
|
|
||||||
|
|
||||||
if(self->no_modifiers_fallback) {
|
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 = gsr_capture_kms_create_egl_image(self, drm_fd, fds, offsets, pitches, modifiers, false);
|
||||||
image = self->params.egl->eglCreateImage(self->params.egl->egl_display, 0, EGL_LINUX_DMA_BUF_EXT, NULL, img_attr);
|
|
||||||
} else {
|
} 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);
|
image = gsr_capture_kms_create_egl_image(self, drm_fd, fds, offsets, pitches, modifiers, true);
|
||||||
while(self->params.egl->eglGetError() != EGL_SUCCESS){}
|
if(!image) {
|
||||||
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) {
|
|
||||||
fprintf(stderr, "gsr error: gsr_capture_kms_capture: failed to create egl image with modifiers, trying without modifiers\n");
|
fprintf(stderr, "gsr error: gsr_capture_kms_capture: failed to create egl image with modifiers, trying without modifiers\n");
|
||||||
self->no_modifiers_fallback = true;
|
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 = gsr_capture_kms_create_egl_image(self, drm_fd, fds, offsets, pitches, modifiers, false);
|
||||||
image = self->params.egl->eglCreateImage(self->params.egl->egl_display, 0, EGL_LINUX_DMA_BUF_EXT, NULL, img_attr);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while(self->params.egl->glGetError() != 0){}
|
if(self->external_texture_fallback) {
|
||||||
if(self->params.egl->glGetError() != 0)
|
gsr_capture_kms_bind_image_to_texture(self, image, self->external_input_texture_id, true);
|
||||||
fprintf(stderr, "kms error 1\n");
|
} else {
|
||||||
self->params.egl->glBindTexture(GL_TEXTURE_EXTERNAL_OES, self->input_texture_id);
|
if(!gsr_capture_kms_bind_image_to_texture(self, image, self->input_texture_id, false)) {
|
||||||
if(self->params.egl->glGetError() != 0)
|
fprintf(stderr, "gsr error: gsr_pipewire_map_texture: failed to bind image to texture, trying with external texture\n");
|
||||||
fprintf(stderr, "kms error 2\n");
|
self->external_texture_fallback = true;
|
||||||
self->params.egl->glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, image);
|
gsr_capture_kms_bind_image_to_texture(self, image, self->external_input_texture_id, true);
|
||||||
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);
|
|
||||||
|
|
||||||
vec2i capture_pos = self->capture_pos;
|
vec2i capture_pos = self->capture_pos;
|
||||||
if(!capture_is_combined_plane)
|
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_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);
|
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,
|
(vec2i){target_x, target_y}, self->capture_size,
|
||||||
capture_pos, 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) {
|
if(self->params.record_cursor && cursor_drm_fd) {
|
||||||
const vec2i cursor_size = {cursor_drm_fd->width, cursor_drm_fd->height};
|
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) {
|
static bool gsr_capture_kms_uses_external_image(gsr_capture *cap) {
|
||||||
gsr_capture_kms *self = cap->priv;
|
(void)cap;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,8 +15,7 @@
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
gsr_capture_portal_params params;
|
gsr_capture_portal_params params;
|
||||||
|
|
||||||
unsigned int input_texture_id;
|
gsr_texture_map texture_map;
|
||||||
unsigned int cursor_texture_id;
|
|
||||||
|
|
||||||
gsr_dbus dbus;
|
gsr_dbus dbus;
|
||||||
char *session_handle;
|
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) {
|
static void gsr_capture_portal_stop(gsr_capture_portal *self) {
|
||||||
if(self->input_texture_id) {
|
if(self->texture_map.texture_id) {
|
||||||
self->params.egl->glDeleteTextures(1, &self->input_texture_id);
|
self->params.egl->glDeleteTextures(1, &self->texture_map.texture_id);
|
||||||
self->input_texture_id = 0;
|
self->texture_map.texture_id = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(self->cursor_texture_id) {
|
if(self->texture_map.external_texture_id) {
|
||||||
self->params.egl->glDeleteTextures(1, &self->cursor_texture_id);
|
self->params.egl->glDeleteTextures(1, &self->texture_map.external_texture_id);
|
||||||
self->cursor_texture_id = 0;
|
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);
|
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) {
|
static void gsr_capture_portal_create_input_textures(gsr_capture_portal *self) {
|
||||||
self->params.egl->glGenTextures(1, &self->input_texture_id);
|
self->params.egl->glGenTextures(1, &self->texture_map.texture_id);
|
||||||
self->params.egl->glBindTexture(GL_TEXTURE_2D, self->input_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_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_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_MIN_FILTER, GL_LINEAR);
|
||||||
self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_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->glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
|
||||||
self->params.egl->glGenTextures(1, &self->cursor_texture_id);
|
self->params.egl->glGenTextures(1, &self->texture_map.external_texture_id);
|
||||||
self->params.egl->glBindTexture(GL_TEXTURE_2D, self->cursor_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_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_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_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();
|
const double start_time = clock_get_monotonic_seconds();
|
||||||
while(clock_get_monotonic_seconds() - start_time < 5.0) {
|
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);
|
gsr_capture_portal_cleanup_plane_fds(self);
|
||||||
self->capture_size.x = region.width;
|
self->capture_size.x = region.width;
|
||||||
self->capture_size.y = region.height;
|
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) */
|
/* TODO: Handle formats other than RGB(a) */
|
||||||
gsr_pipewire_region region = {0, 0, 0, 0};
|
gsr_pipewire_region region = {0, 0, 0, 0};
|
||||||
gsr_pipewire_region cursor_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) {
|
if(region.width != self->capture_size.x || region.height != self->capture_size.y) {
|
||||||
gsr_color_conversion_clear(color_conversion);
|
gsr_color_conversion_clear(color_conversion);
|
||||||
self->capture_size.x = region.width;
|
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_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);
|
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){target_x, target_y}, self->capture_size,
|
||||||
(vec2i){region.x, region.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) {
|
if(self->params.record_cursor) {
|
||||||
const vec2i cursor_pos = {
|
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->glEnable(GL_SCISSOR_TEST);
|
||||||
self->params.egl->glScissor(target_x, target_y, self->capture_size.x, self->capture_size.y);
|
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){cursor_pos.x, cursor_pos.y}, (vec2i){cursor_region.width, cursor_region.height},
|
||||||
(vec2i){0, 0}, (vec2i){cursor_region.width, cursor_region.height},
|
(vec2i){0, 0}, (vec2i){cursor_region.width, cursor_region.height},
|
||||||
0.0f, false);
|
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;
|
return GSR_SOURCE_COLOR_RGB;
|
||||||
}
|
}
|
||||||
|
|
||||||
// static bool gsr_capture_portal_uses_external_image(gsr_capture *cap) {
|
static bool gsr_capture_portal_uses_external_image(gsr_capture *cap) {
|
||||||
// gsr_capture_portal *cap_portal = cap->priv;
|
(void)cap;
|
||||||
// return cap_portal->params.egl->gpu_info.vendor == GSR_GPU_VENDOR_NVIDIA;
|
return true;
|
||||||
// }
|
}
|
||||||
|
|
||||||
static void gsr_capture_portal_destroy(gsr_capture *cap, AVCodecContext *video_codec_context) {
|
static void gsr_capture_portal_destroy(gsr_capture *cap, AVCodecContext *video_codec_context) {
|
||||||
(void)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 = gsr_capture_portal_capture,
|
||||||
.capture_end = gsr_capture_portal_capture_end,
|
.capture_end = gsr_capture_portal_capture_end,
|
||||||
.get_source_color = gsr_capture_portal_get_source_color,
|
.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,
|
.destroy = gsr_capture_portal_destroy,
|
||||||
.priv = cap_portal
|
.priv = cap_portal
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1782,8 +1782,8 @@ static void info_command() {
|
|||||||
egl.card_path[0] = '\0';
|
egl.card_path[0] = '\0';
|
||||||
if(monitor_capture_use_drm(&egl, wayland)) {
|
if(monitor_capture_use_drm(&egl, wayland)) {
|
||||||
// TODO: Allow specifying another card, and in other places
|
// TODO: Allow specifying another card, and in other places
|
||||||
if(!gsr_get_valid_card_path(&egl, egl.card_path, true)) {
|
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 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\n");
|
||||||
_exit(23);
|
_exit(23);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2470,7 +2470,7 @@ int main(int argc, char **argv) {
|
|||||||
_exit(1);
|
_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;
|
gsr_egl egl;
|
||||||
if(!gsr_egl_load(&egl, dpy, wayland, is_monitor_capture)) {
|
if(!gsr_egl_load(&egl, dpy, wayland, is_monitor_capture)) {
|
||||||
fprintf(stderr, "gsr error: failed to load opengl\n");
|
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)) {
|
if(monitor_capture_use_drm(&egl, wayland)) {
|
||||||
// TODO: Allow specifying another card, and in other places
|
// TODO: Allow specifying another card, and in other places
|
||||||
if(!gsr_get_valid_card_path(&egl, egl.card_path, is_monitor_capture)) {
|
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);
|
_exit(2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
for(int i = 0; i < GSR_PIPEWIRE_DMABUF_MAX_PLANES; ++i) {
|
||||||
plane_fds[i] = -1;
|
plane_fds[i] = -1;
|
||||||
}
|
}
|
||||||
*num_plane_fds = 0;
|
*num_plane_fds = 0;
|
||||||
|
*using_external_image = false;
|
||||||
pthread_mutex_lock(&self->mutex);
|
pthread_mutex_lock(&self->mutex);
|
||||||
|
|
||||||
if(!self->negotiated || self->dmabuf_data[0].fd <= 0) {
|
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;
|
EGLImage image = NULL;
|
||||||
intptr_t img_attr[44];
|
|
||||||
|
|
||||||
if(self->no_modifiers_fallback) {
|
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,
|
image = gsr_pipewire_create_egl_image(self, fds, offsets, pitches, modifiers, false);
|
||||||
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);
|
|
||||||
} else {
|
} 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,
|
image = gsr_pipewire_create_egl_image(self, fds, offsets, pitches, modifiers, true);
|
||||||
fds, offsets, pitches, modifiers, self->dmabuf_num_planes, true);
|
if(!image) {
|
||||||
|
|
||||||
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) {
|
|
||||||
fprintf(stderr, "gsr error: gsr_pipewire_map_texture: failed to create egl image with modifiers, trying without modifiers\n");
|
fprintf(stderr, "gsr error: gsr_pipewire_map_texture: failed to create egl image with modifiers, trying without modifiers\n");
|
||||||
self->no_modifiers_fallback = true;
|
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,
|
image = gsr_pipewire_create_egl_image(self, fds, offsets, pitches, modifiers, false);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self->egl->glBindTexture(GL_TEXTURE_2D, texture_id);
|
if(self->external_texture_fallback) {
|
||||||
self->egl->glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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->eglDestroyImage(self->egl->egl_display, image);
|
||||||
self->egl->glBindTexture(GL_TEXTURE_2D, 0);
|
self->egl->glBindTexture(texture_target, 0);
|
||||||
|
|
||||||
if(self->cursor.data) {
|
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
|
// 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->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);
|
self->egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
|
|||||||
Reference in New Issue
Block a user