From fcb45b82f24bbd6fc433670a47361b1469c918ff Mon Sep 17 00:00:00 2001 From: dec05eba Date: Sat, 6 Sep 2025 01:17:56 +0200 Subject: [PATCH] Re-add portal damage tracking (-fm content) --- README.md | 2 +- TODO | 12 ----------- src/main.cpp | 5 ++--- src/pipewire_video.c | 51 ++++++++++++++++++++++---------------------- 4 files changed, 29 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 327943c..268b14d 100644 --- a/README.md +++ b/README.md @@ -173,7 +173,7 @@ Build GPU Screen Recorder with the `-Dplugin_examples=true` meson option to buil If you record at your monitors refresh rate and enabled vsync in a game then there might be a desync between the game updating a frame and GPU Screen Recorder capturing a frame. This is an issue in some games. If you experience this issue then you might want to either disable vsync in the game or use the `-fm content` option to sync capture to the content on the screen. For example: `gpu-screen-recorder -w screen -fm content -o video.mp4`.\ -Note that this option is currently only available on X11 (it's not really possible to do on Wayland or it's a hit or miss). +Note that this option is currently only available on X11, or with desktop portal capture on Wayland (`-w portal`). # Issues ## NVIDIA Nvidia drivers have an issue where CUDA breaks if CUDA is running when suspend/hibernation happens, and it remains broken until you reload the nvidia driver. `extra/gsr-nvidia.conf` will be installed by default when you install GPU Screen Recorder and that should fix this issue. If this doesn't fix the issue for you then your distro may use a different path for modprobe files. In that case you have to install that `extra/gsr-nvidia.conf` yourself into that location. diff --git a/TODO b/TODO index 6ac52b5..6a6ec9b 100644 --- a/TODO +++ b/TODO @@ -71,26 +71,16 @@ Test if p2 state can be worked around by using pure nvenc api and overwriting cu Drop frames if live streaming cant keep up with target fps, or dynamically change resolution/quality. -Support low power option. - Instead of sending a big list of drm data back to kms client, send the monitor we want to record to kms server and the server should respond with only the matching monitor, and cursor. Tonemap hdr to sdr when hdr is enabled and when hevc_hdr/av1_hdr is not used. -Add 10 bit record option, h264_10bit, hevc_10bit and av1_10bit. - Rotate cursor texture properly (around top left origin). -Setup hardware video context so we can query constraints and capabilities for better default and better error messages. - -Use CAP_SYS_NICE in flatpak too on the main gpu screen recorder binary. It makes recording smoother, especially with constant framerate. - Modify ffmpeg to accept opengl texture for nvenc encoding. Removes extra buffers and copies. When vulkan encode is added, mention minimum nvidia driver required. (550.54.14?). -Support drm plane rotation. Neither X11 nor any Wayland compositor currently rotates drm planes so this might not be needed. - Investigate if there is a way to do gpu->gpu copy directly without touching system ram to enable video encoding on a different gpu. On nvidia this is possible with cudaMemcpyPeer, but how about from an intel/amd gpu to an nvidia gpu or the other way around or any combination of iGPU and dedicated GPU? Maybe something with clEnqueueMigrateMemObjects? on AMD something with DirectGMA maybe? @@ -331,7 +321,5 @@ Add support for QVBR (QP with target bitrate). KDE Plasma Wayland seems to use overlay planes now in non-fullscreen mode(limited to 1 overlay plane per gpu). Check if this is the case in the latest kde on arch linux. If it is, then support it in kms capture. -Support -fm content with pipewire (enable damage tracking in pipewire again and check if it actually works). - Check if pipewire audio link-factory is available before attempting to use app audio or merging audio with pipewire. Also do the same in supports_app_audio check in gpu-screen-recorder --info output. \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index e3bc3ce..ef66f0d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3701,15 +3701,14 @@ int main(int argc, char **argv) { bool hdr_metadata_set = false; const bool hdr = video_codec_is_hdr(arg_parser.video_codec); - double damage_timeout_seconds = arg_parser.framerate_mode == GSR_FRAMERATE_MODE_CONTENT ? 0.5 : 0.1; - damage_timeout_seconds = std::max(damage_timeout_seconds, target_fps); - bool use_damage_tracking = false; gsr_damage damage; memset(&damage, 0, sizeof(damage)); if(gsr_window_get_display_server(window) == GSR_DISPLAY_SERVER_X11) { gsr_damage_init(&damage, &egl, arg_parser.record_cursor); use_damage_tracking = true; + } else if(!capture->is_damaged) { + fprintf(stderr, "gsr warning: \"-fm content\" has no effect on Wayland when recording a monitor. Either record a monitor on X11 or capture with desktop portal instead (-w portal)\n"); } if(is_monitor_capture) diff --git a/src/pipewire_video.c b/src/pipewire_video.c index ff267cd..46cd00f 100644 --- a/src/pipewire_video.c +++ b/src/pipewire_video.c @@ -116,8 +116,6 @@ static const struct pw_core_events core_events = { static void on_process_cb(void *user_data) { gsr_pipewire_video *self = user_data; - struct spa_meta_cursor *cursor = NULL; - //struct spa_meta *video_damage = NULL; /* Find the most recent buffer */ struct pw_buffer *pw_buf = NULL; @@ -137,12 +135,11 @@ static void on_process_cb(void *user_data) { struct spa_buffer *buffer = pw_buf->buffer; const bool has_buffer = buffer->datas[0].chunk->size != 0; - if(!has_buffer) - goto read_metadata; pthread_mutex_lock(&self->mutex); - if(buffer->datas[0].type == SPA_DATA_DmaBuf) { + bool buffer_updated = false; + if(has_buffer && buffer->datas[0].type == SPA_DATA_DmaBuf) { for(size_t i = 0; i < self->dmabuf_num_planes; ++i) { if(self->dmabuf_data[i].fd > 0) { close(self->dmabuf_data[i].fd); @@ -160,9 +157,7 @@ static void on_process_cb(void *user_data) { self->dmabuf_data[i].stride = buffer->datas[i].chunk->stride; } - self->damaged = true; - } else { - // TODO: + buffer_updated = true; } // TODO: Move down to read_metadata @@ -201,32 +196,34 @@ static void on_process_cb(void *user_data) { break; } - pthread_mutex_unlock(&self->mutex); + const struct spa_meta *video_damage = spa_buffer_find_meta(buffer, SPA_META_VideoDamage); + if(video_damage) { + struct spa_meta_region *meta_region = NULL; + spa_meta_for_each(meta_region, video_damage) { + if(meta_region->region.size.width == 0 || meta_region->region.size.height == 0) + continue; -read_metadata: + //fprintf(stderr, "video damage: %dx%d %dx%d\n", meta_region->region.position.x, meta_region->region.position.y, meta_region->region.size.width, meta_region->region.size.height); + self->damaged = true; + break; + } - // video_damage = spa_buffer_find_meta(buffer, SPA_META_VideoDamage); - // if(video_damage) { - // struct spa_meta_region *r = spa_meta_first(video_damage); - // if(spa_meta_check(r, video_damage)) { - // //fprintf(stderr, "damage: %d,%d %ux%u\n", r->region.position.x, r->region.position.y, r->region.size.width, r->region.size.height); - // pthread_mutex_lock(&self->mutex); - // self->damaged = true; - // pthread_mutex_unlock(&self->mutex); - // } - // } + if(!self->damaged) + fprintf(stderr, "has damage: %s\n", self->damaged ? "yes" : "no"); + } else if(buffer_updated) { + self->damaged = true; + } - cursor = spa_buffer_find_meta_data(buffer, SPA_META_Cursor, sizeof(*cursor)); + const struct spa_meta_cursor *cursor = spa_buffer_find_meta_data(buffer, SPA_META_Cursor, sizeof(*cursor)); self->cursor.valid = cursor && spa_meta_cursor_is_valid(cursor); if (self->cursor.visible && self->cursor.valid) { - pthread_mutex_lock(&self->mutex); - struct spa_meta_bitmap *bitmap = NULL; if (cursor->bitmap_offset) bitmap = SPA_MEMBER(cursor, cursor->bitmap_offset, struct spa_meta_bitmap); - if (bitmap && bitmap->size.width > 0 && bitmap->size.height && is_cursor_format_supported(bitmap->format)) { + // TODO: Maybe check if the cursor is actually visible by checking if there are visible pixels + if (bitmap && bitmap->size.width > 0 && bitmap->size.height > 0 && is_cursor_format_supported(bitmap->format)) { const uint8_t *bitmap_data = SPA_MEMBER(bitmap, bitmap->offset, uint8_t); fprintf(stderr, "gsr info: pipewire: cursor bitmap update, size: %dx%d, format: %s\n", (int)bitmap->size.width, (int)bitmap->size.height, spa_debug_type_find_name(spa_type_video_format, bitmap->format)); @@ -243,15 +240,19 @@ read_metadata: self->cursor.hotspot_y = cursor->hotspot.y; self->cursor.width = bitmap->size.width; self->cursor.height = bitmap->size.height; + self->damaged = true; } + if(cursor->position.x != self->cursor.x || cursor->position.y != self->cursor.y) + self->damaged = true; + self->cursor.x = cursor->position.x; self->cursor.y = cursor->position.y; - pthread_mutex_unlock(&self->mutex); //fprintf(stderr, "gsr info: pipewire: cursor: %d %d %d %d\n", cursor->hotspot.x, cursor->hotspot.y, cursor->position.x, cursor->position.y); } + pthread_mutex_unlock(&self->mutex); pw_stream_queue_buffer(self->stream, pw_buf); }