aboutsummaryrefslogtreecommitdiff
path: root/src/capture/xcomposite.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/capture/xcomposite.c')
-rw-r--r--src/capture/xcomposite.c127
1 files changed, 91 insertions, 36 deletions
diff --git a/src/capture/xcomposite.c b/src/capture/xcomposite.c
index 47daa99..3240ed8 100644
--- a/src/capture/xcomposite.c
+++ b/src/capture/xcomposite.c
@@ -6,6 +6,7 @@
#include <unistd.h>
#include <assert.h>
#include <X11/Xlib.h>
+#include <X11/extensions/Xdamage.h>
#include <libavutil/hwcontext.h>
#include <libavutil/hwcontext.h>
#include <libavutil/frame.h>
@@ -36,6 +37,23 @@ static Window get_focused_window(Display *display, Atom net_active_window_atom)
return None;
}
+static void gsr_capture_xcomposite_setup_damage(gsr_capture_xcomposite *self, Window window) {
+ if(self->damage_event == 0)
+ return;
+
+ if(self->damage) {
+ XDamageDestroy(self->params.egl->x11.dpy, self->damage);
+ self->damage = None;
+ }
+
+ self->damage = XDamageCreate(self->params.egl->x11.dpy, window, XDamageReportNonEmpty);
+ if(self->damage) {
+ XDamageSubtract(self->params.egl->x11.dpy, self->damage, None, None);
+ } else {
+ fprintf(stderr, "gsr warning: gsr_capture_xcomposite_setup_damage: XDamageCreate failed\n");
+ }
+}
+
int gsr_capture_xcomposite_start(gsr_capture_xcomposite *self, AVCodecContext *video_codec_context, AVFrame *frame) {
self->base.video_codec_context = video_codec_context;
self->base.egl = self->params.egl;
@@ -51,6 +69,20 @@ int gsr_capture_xcomposite_start(gsr_capture_xcomposite *self, AVCodecContext *v
self->window = self->params.window;
}
+ if(self->params.track_damage) {
+ if(!XDamageQueryExtension(self->params.egl->x11.dpy, &self->damage_event, &self->damage_error)) {
+ fprintf(stderr, "gsr warning: gsr_capture_xcomposite_start: XDamage is not supported by your X11 server\n");
+ self->damage_event = 0;
+ self->damage_error = 0;
+ }
+ } else {
+ self->damage_event = 0;
+ self->damage_error = 0;
+ }
+
+ self->damaged = true;
+ gsr_capture_xcomposite_setup_damage(self, self->window);
+
/* TODO: Do these in tick, and allow error if follow_focused */
XWindowAttributes attr;
@@ -107,6 +139,16 @@ int gsr_capture_xcomposite_start(gsr_capture_xcomposite *self, AVCodecContext *v
// TODO: dont do this if using ffmpeg reports that this is not needed (AMD driver bug that was fixed recently)
video_codec_context->width = FFALIGN(video_size.x, 64);
video_codec_context->height = FFALIGN(video_size.y, 16);
+ } else if(self->params.egl->gpu_info.vendor == GSR_GPU_VENDOR_AMD && video_codec_context->codec_id == AV_CODEC_ID_AV1) {
+ // TODO: Dont do this for VCN 5 and forward which should fix this hardware bug
+ video_codec_context->width = FFALIGN(video_size.x, 64);
+ // AMD driver has special case handling for 1080 height to set it to 1082 instead of 1088 (1080 aligned to 16).
+ // TODO: Set height to 1082 in this case, but it wont work because it will be aligned to 1088.
+ if(video_size.y == 1080) {
+ video_codec_context->height = 1080;
+ } else {
+ video_codec_context->height = FFALIGN(video_size.y, 16);
+ }
} else {
video_codec_context->width = FFALIGN(video_size.x, 2);
video_codec_context->height = FFALIGN(video_size.y, 2);
@@ -116,11 +158,15 @@ int gsr_capture_xcomposite_start(gsr_capture_xcomposite *self, AVCodecContext *v
frame->height = video_codec_context->height;
self->window_resize_timer = clock_get_monotonic_seconds();
- self->clear_next_frame = true;
return 0;
}
void gsr_capture_xcomposite_stop(gsr_capture_xcomposite *self) {
+ if(self->damage) {
+ XDamageDestroy(self->params.egl->x11.dpy, self->damage);
+ self->damage = None;
+ }
+
window_texture_deinit(&self->window_texture);
gsr_cursor_deinit(&self->cursor);
gsr_capture_base_stop(&self->base);
@@ -171,7 +217,20 @@ void gsr_capture_xcomposite_tick(gsr_capture_xcomposite *self, AVCodecContext *v
}
}
- gsr_cursor_update(&self->cursor, &self->xev);
+ if(self->damage_event && self->xev.type == self->damage_event + XDamageNotify) {
+ XDamageNotifyEvent *de = (XDamageNotifyEvent*)&self->xev;
+ XserverRegion region = XFixesCreateRegion(self->params.egl->x11.dpy, NULL, 0);
+ // Subtract all the damage, repairing the window
+ XDamageSubtract(self->params.egl->x11.dpy, de->damage, None, region);
+ XFixesDestroyRegion(self->params.egl->x11.dpy, region);
+ self->damaged = true;
+ }
+
+ if(gsr_cursor_update(&self->cursor, &self->xev)) {
+ if(self->params.record_cursor && self->cursor.visible) {
+ self->damaged = true;
+ }
+ }
}
if(self->params.follow_focused && !self->follow_focused_initialized) {
@@ -198,6 +257,7 @@ void gsr_capture_xcomposite_tick(gsr_capture_xcomposite *self, AVCodecContext *v
window_texture_deinit(&self->window_texture);
window_texture_init(&self->window_texture, self->params.egl->x11.dpy, self->window, self->params.egl); // TODO: Do not do the below window_texture_on_resize after this
+ gsr_capture_xcomposite_setup_damage(self, self->window);
}
}
@@ -221,9 +281,18 @@ void gsr_capture_xcomposite_tick(gsr_capture_xcomposite *self, AVCodecContext *v
self->params.egl->glBindTexture(GL_TEXTURE_2D, 0);
gsr_color_conversion_clear(&self->base.color_conversion);
+ gsr_capture_xcomposite_setup_damage(self, self->window);
}
}
+bool gsr_capture_xcomposite_is_damaged(gsr_capture_xcomposite *self) {
+ return self->damage_event ? self->damaged : true;
+}
+
+void gsr_capture_xcomposite_clear_damage(gsr_capture_xcomposite *self) {
+ self->damaged = false;
+}
+
bool gsr_capture_xcomposite_should_stop(gsr_capture_xcomposite *self, bool *err) {
if(self->should_stop) {
if(err)
@@ -242,50 +311,36 @@ int gsr_capture_xcomposite_capture(gsr_capture_xcomposite *self, AVFrame *frame)
const int target_x = max_int(0, frame->width / 2 - self->texture_size.x / 2);
const int target_y = max_int(0, frame->height / 2 - self->texture_size.y / 2);
- // TODO: Can we do this a better way than to call it every capture?
- if(self->params.record_cursor)
- gsr_cursor_tick(&self->cursor, self->window);
-
const vec2i cursor_pos = {
target_x + self->cursor.position.x - self->cursor.hotspot.x,
target_y + self->cursor.position.y - self->cursor.hotspot.y
};
- const bool cursor_completely_inside_window =
- cursor_pos.x >= target_x &&
- cursor_pos.x + self->cursor.size.x <= target_x + self->texture_size.x &&
- cursor_pos.y >= target_y &&
- cursor_pos.y + self->cursor.size.y <= target_y + self->texture_size.y;
-
- const bool cursor_inside_window =
- cursor_pos.x + self->cursor.size.x >= target_x &&
- cursor_pos.x <= target_x + self->texture_size.x &&
- cursor_pos.y + self->cursor.size.y >= target_y &&
- cursor_pos.y <= target_y + self->texture_size.y;
-
- if(self->clear_next_frame) {
- self->clear_next_frame = false;
- gsr_color_conversion_clear(&self->base.color_conversion);
- }
-
- /*
- We dont draw the cursor if it's outside the window but if it's partially inside the window then the cursor area that is outside the window
- will not get overdrawn the next frame causing a cursor trail to be visible since we dont clear the background.
- To fix this we detect if the cursor is partially inside the window and clear the background only in that case.
- */
- if(!cursor_completely_inside_window && cursor_inside_window && self->params.record_cursor)
- self->clear_next_frame = true;
-
gsr_color_conversion_draw(&self->base.color_conversion, window_texture_get_opengl_texture_id(&self->window_texture),
(vec2i){target_x, target_y}, self->texture_size,
(vec2i){0, 0}, self->texture_size,
0.0f, false);
- if(cursor_inside_window && self->params.record_cursor) {
- gsr_color_conversion_draw(&self->base.color_conversion, self->cursor.texture_id,
- cursor_pos, self->cursor.size,
- (vec2i){0, 0}, self->cursor.size,
- 0.0f, false);
+ if(self->params.record_cursor && self->cursor.visible) {
+ gsr_cursor_tick(&self->cursor, self->window);
+
+ const bool cursor_inside_window =
+ cursor_pos.x + self->cursor.size.x >= target_x &&
+ cursor_pos.x <= target_x + self->texture_size.x &&
+ cursor_pos.y + self->cursor.size.y >= target_y &&
+ cursor_pos.y <= target_y + self->texture_size.y;
+
+ if(cursor_inside_window) {
+ self->base.egl->glEnable(GL_SCISSOR_TEST);
+ self->base.egl->glScissor(target_x, target_y, self->texture_size.x, self->texture_size.y);
+
+ gsr_color_conversion_draw(&self->base.color_conversion, self->cursor.texture_id,
+ cursor_pos, self->cursor.size,
+ (vec2i){0, 0}, self->cursor.size,
+ 0.0f, false);
+
+ self->base.egl->glDisable(GL_SCISSOR_TEST);
+ }
}
self->params.egl->eglSwapBuffers(self->params.egl->egl_display, self->params.egl->egl_surface);