add tearing patch

This commit is contained in:
korei999 2024-08-07 18:32:55 +03:00
parent 5453b74075
commit cd632cec2d
2 changed files with 418 additions and 0 deletions

23
patches/tearing/README.md Normal file
View File

@ -0,0 +1,23 @@
### Description
This patch adds support for tearing protocol. To get it working `export WLR_DRM_NO_ATOMIC=1` is probably required.
Setting `ForceTearingRule` is also probably required since surfaces always receive presentation hint 0 (VSYNC) as far as i can tell.
Set rules in the config.h (exact string match):
```
static const ForceTearingRule force_tearing[] = {
{.title = "", .appid = "oni.exe"},
{.title = "", .appid = "hl_linux"},
{.title = "", .appid = "steam_app_210970"},
};
```
### Download
- [git branch](https://codeberg.org/korei999/dwl/src/branch/globalkey)
- [2024-06-08](https://codeberg.org/dwl/dwl-patches/src/branch/main/patches/globalkey/globalkey.patch)
### Download
- [git branch](https://codeberg.org/korei999/dwl/src/branch/tearing)
- [2024-06-08](https://codeberg.org/dwl/dwl-patches/src/branch/main/patches/tearing/tearing.patch)
### Authors
- [korei999](https://codeberg.org/korei999)

View File

@ -0,0 +1,395 @@
From 490b66b2301c3eab6e9178500998f0b9ead97a5a Mon Sep 17 00:00:00 2001
From: korei999 <ju7t1xe@gmail.com>
Date: Wed, 7 Aug 2024 17:35:57 +0300
Subject: [PATCH] implement tearing protocol
---
Makefile | 7 +-
config.def.h | 5 ++
dwl.c | 211 +++++++++++++++++++++++++++++++++++++++++++--------
3 files changed, 190 insertions(+), 33 deletions(-)
diff --git a/Makefile b/Makefile
index f3e1f10..0d1b41a 100644
--- a/Makefile
+++ b/Makefile
@@ -12,7 +12,7 @@ DWLDEVCFLAGS = -g -Wpedantic -Wall -Wextra -Wdeclaration-after-statement \
-Wfloat-conversion
# CFLAGS / LDFLAGS
-PKGS = wlroots-0.19 wayland-server xkbcommon libinput $(XLIBS)
+PKGS = wlroots-0.19 wayland-server xkbcommon libinput pixman-1 $(XLIBS)
DWLCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(DWLCPPFLAGS) $(DWLDEVCFLAGS) $(CFLAGS)
LDLIBS = `$(PKG_CONFIG) --libs $(PKGS)` -lm $(LIBS)
@@ -21,7 +21,7 @@ dwl: dwl.o util.o
$(CC) dwl.o util.o $(DWLCFLAGS) $(LDFLAGS) $(LDLIBS) -o $@
dwl.o: dwl.c client.h config.h config.mk cursor-shape-v1-protocol.h \
pointer-constraints-unstable-v1-protocol.h wlr-layer-shell-unstable-v1-protocol.h \
- wlr-output-power-management-unstable-v1-protocol.h xdg-shell-protocol.h
+ wlr-output-power-management-unstable-v1-protocol.h xdg-shell-protocol.h tearing-control-v1-protocol.h
util.o: util.c util.h
# wayland-scanner is a tool which generates C headers and rigging for Wayland
@@ -45,6 +45,9 @@ wlr-output-power-management-unstable-v1-protocol.h:
xdg-shell-protocol.h:
$(WAYLAND_SCANNER) server-header \
$(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@
+tearing-control-v1-protocol.h:
+ $(WAYLAND_SCANNER) server-header \
+ $(WAYLAND_PROTOCOLS)/staging/tearing-control/tearing-control-v1.xml $@
config.h:
cp config.def.h $@
diff --git a/config.def.h b/config.def.h
index 22d2171..c928330 100644
--- a/config.def.h
+++ b/config.def.h
@@ -28,6 +28,11 @@ static const Rule rules[] = {
{ "firefox_EXAMPLE", NULL, 1 << 8, 0, -1 }, /* Start on ONLY tag "9" */
};
+static const ForceTearingRule force_tearing[] = {
+ {.title = "", .appid = "oni.exe"},
+ {.title = "", .appid = "hl_linux"},
+};
+
/* layout(s) */
static const Layout layouts[] = {
/* symbol arrange function */
diff --git a/dwl.c b/dwl.c
index 72892d9..91b2708 100644
--- a/dwl.c
+++ b/dwl.c
@@ -51,6 +51,7 @@
#include <wlr/types/wlr_session_lock_v1.h>
#include <wlr/types/wlr_single_pixel_buffer_v1.h>
#include <wlr/types/wlr_subcompositor.h>
+#include <wlr/types/wlr_tearing_control_v1.h>
#include <wlr/types/wlr_viewporter.h>
#include <wlr/types/wlr_virtual_keyboard_v1.h>
#include <wlr/types/wlr_virtual_pointer_v1.h>
@@ -90,6 +91,11 @@ enum { NetWMWindowTypeDialog, NetWMWindowTypeSplash, NetWMWindowTypeToolbar,
NetWMWindowTypeUtility, NetLast }; /* EWMH atoms */
#endif
+typedef struct ForceTearingRule {
+ const char* title;
+ const char* appid;
+} ForceTearingRule;
+
typedef union {
int i;
uint32_t ui;
@@ -142,6 +148,7 @@ typedef struct {
uint32_t tags;
int isfloating, isurgent, isfullscreen;
uint32_t resize; /* configure serial of a pending resize */
+ enum wp_tearing_control_v1_presentation_hint tearing_hint;
} Client;
typedef struct {
@@ -243,6 +250,19 @@ typedef struct {
struct wl_listener destroy;
} SessionLock;
+typedef struct TearingController {
+ struct wlr_tearing_control_v1 *tearing_control;
+ struct wl_listener set_hint;
+ struct wl_listener destroy;
+
+ struct wl_list link; /* tearing_controllers */
+} TearingController;
+
+typedef struct SendFrameDoneData {
+ struct timespec when;
+ struct Monitor *mon;
+} SendFrameDoneData;
+
/* function declarations */
static void applybounds(Client *c, struct wlr_box *bbox);
static void applyrules(Client *c);
@@ -256,6 +276,7 @@ static void chvt(const Arg *arg);
static void checkidleinhibitor(struct wlr_surface *exclude);
static void cleanup(void);
static void cleanupmon(struct wl_listener *listener, void *data);
+static bool clientcantear(Client* c);
static void closemon(Monitor *m);
static void commitlayersurfacenotify(struct wl_listener *listener, void *data);
static void commitnotify(struct wl_listener *listener, void *data);
@@ -293,6 +314,9 @@ static Client *focustop(Monitor *m);
static void fullscreennotify(struct wl_listener *listener, void *data);
static void gpureset(struct wl_listener *listener, void *data);
static void handlesig(int signo);
+static void handletearingcontrollersethint(struct wl_listener *listener, void *data);
+static void handletearingcontrollerdestroy(struct wl_listener *listener, void *data);
+static void handlenewtearinghint(struct wl_listener *listener, void *data);
static void incnmaster(const Arg *arg);
static void inputdevice(struct wl_listener *listener, void *data);
static int keybinding(uint32_t mods, xkb_keysym_t sym);
@@ -309,6 +333,7 @@ static void motionnotify(uint32_t time, struct wlr_input_device *device, double
double sy, double sx_unaccel, double sy_unaccel);
static void motionrelative(struct wl_listener *listener, void *data);
static void moveresize(const Arg *arg);
+static bool outputcantear(Monitor* m);
static void outputmgrapply(struct wl_listener *listener, void *data);
static void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test);
static void outputmgrtest(struct wl_listener *listener, void *data);
@@ -323,6 +348,7 @@ static void requeststartdrag(struct wl_listener *listener, void *data);
static void requestmonstate(struct wl_listener *listener, void *data);
static void resize(Client *c, struct wlr_box geo, int interact);
static void run(char *startup_cmd);
+static void sendframedoneiterator(struct wlr_scene_buffer *buffer, int x, int y, void *user_data);
static void setcursor(struct wl_listener *listener, void *data);
static void setcursorshape(struct wl_listener *listener, void *data);
static void setfloating(Client *c, int floating);
@@ -403,6 +429,10 @@ static struct wlr_scene_rect *locked_bg;
static struct wlr_session_lock_v1 *cur_lock;
static struct wl_listener lock_listener = {.notify = locksession};
+struct wlr_tearing_control_manager_v1 *tearing_control_v1;
+struct wl_listener tearing_control_new_object;
+struct wl_list tearing_controllers;
+
static struct wlr_seat *seat;
static KeyboardGroup *kb_group;
static unsigned int cursor_mode;
@@ -726,6 +756,35 @@ cleanupmon(struct wl_listener *listener, void *data)
free(m);
}
+bool
+clientcantear(Client* c)
+{
+ bool res = false;
+ const char* appid = client_get_appid(c);
+ const char* title = client_get_title(c);
+
+ switch (c->tearing_hint) {
+ case WP_TEARING_CONTROL_V1_PRESENTATION_HINT_VSYNC:
+ res = false;
+ break;
+
+ case WP_TEARING_CONTROL_V1_PRESENTATION_HINT_ASYNC:
+ return true;
+ };
+
+ for (unsigned i = 0; i < LENGTH(force_tearing); i++) {
+ if (appid)
+ if (strcmp(force_tearing[i].appid, appid) == 0)
+ return true;
+
+ if (title)
+ if (strcmp(force_tearing[i].title, title) == 0)
+ return true;
+ }
+
+ return res;
+}
+
void
closemon(Monitor *m)
{
@@ -1517,6 +1576,61 @@ handlesig(int signo)
}
}
+void
+handletearingcontrollersethint(struct wl_listener *listener, void *data)
+{
+ Client *c = NULL, *i = NULL;
+ struct TearingController *controller = wl_container_of(listener, controller, set_hint);
+
+ struct wlr_xdg_surface *surface = wlr_xdg_surface_try_from_wlr_surface(controller->tearing_control->surface);
+
+ /* FIXME: broken appearantly */
+ wl_list_for_each(i, &fstack, flink) {
+ if (VISIBLEON(i, selmon))
+ if (i->surface.xdg == surface) {
+ c = i;
+ break;
+ }
+ }
+
+ if (c) {
+ fprintf(stderr, "FOUND\n");
+ c->tearing_hint = controller->tearing_control->current;
+ }
+}
+
+void
+handletearingcontrollerdestroy(struct wl_listener *listener, void *data)
+{
+ struct TearingController *controller = wl_container_of(listener, controller, destroy);
+ wl_list_remove(&controller->link);
+ free(controller);
+}
+
+void
+handlenewtearinghint(struct wl_listener *listener, void *data)
+{
+ struct wlr_tearing_control_v1 *tearing_control = data;
+ enum wp_tearing_control_v1_presentation_hint hint = wlr_tearing_control_manager_v1_surface_hint_from_surface(tearing_control_v1, tearing_control->surface);
+ struct TearingController *controller = calloc(1, sizeof(struct TearingController));
+
+ fprintf(stderr, "New presentation hint %d received for surface %p\n\n", hint, (void*)tearing_control->surface);
+
+ if (!controller) {
+ fprintf(stderr, "!controller\n");
+ return;
+ }
+
+ controller->tearing_control = tearing_control;
+ controller->set_hint.notify = handletearingcontrollersethint;
+ wl_signal_add(&tearing_control->events.set_hint, &controller->set_hint);
+ controller->destroy.notify = handletearingcontrollerdestroy;
+ wl_signal_add(&tearing_control->events.destroy, &controller->destroy);
+ wl_list_init(&controller->link);
+
+ wl_list_insert(&tearing_controllers, &controller->link);
+}
+
void
incnmaster(const Arg *arg)
{
@@ -1929,6 +2043,16 @@ moveresize(const Arg *arg)
}
}
+bool
+outputcantear(Monitor* m)
+{
+ Client *c = focustop(m);
+ if (c && c->isfullscreen && clientcantear(c))
+ return true;
+
+ return false;
+}
+
void
outputmgrapply(struct wl_listener *listener, void *data)
{
@@ -2104,50 +2228,60 @@ rendermon(struct wl_listener *listener, void *data)
/* This function is called every time an output is ready to display a frame,
* generally at the output's refresh rate (e.g. 60Hz). */
Monitor *m = wl_container_of(listener, m, frame);
- Client *c;
+ struct wlr_scene_output *scene_output = m->scene_output;
+ struct wlr_output *wlr_output = m->wlr_output;
+ struct wlr_gamma_control_v1 *gamma_control = NULL;
struct wlr_output_state pending = {0};
- struct wlr_gamma_control_v1 *gamma_control;
- struct timespec now;
+ SendFrameDoneData frame_done_data = {0};
- /* Render if no XDG clients have an outstanding resize and are visible on
- * this monitor. */
- wl_list_for_each(c, &clients, link) {
- if (c->resize && !c->isfloating && client_is_rendered_on_mon(c, m) && !client_is_stopped(c))
- goto skip;
+ m->wlr_output->frame_pending = false;
+
+ if (!wlr_output->needs_frame && !m->gamma_lut_changed &&
+ !pixman_region32_not_empty(&scene_output->pending_commit_damage)) {
+ goto skip;
+ }
+
+ wlr_output_state_init(&pending);
+ if (!wlr_scene_output_build_state(m->scene_output, &pending, NULL)) {
+ wlr_output_state_finish(&pending);
+ goto skip;
}
- /*
- * HACK: The "correct" way to set the gamma is to commit it together with
- * the rest of the state in one go, but to do that we would need to rewrite
- * wlr_scene_output_commit() in order to add the gamma to the pending
- * state before committing, instead try to commit the gamma in one frame,
- * and commit the rest of the state in the next one (or in the same frame if
- * the gamma can not be committed).
- */
if (m->gamma_lut_changed) {
- gamma_control
- = wlr_gamma_control_manager_v1_get_control(gamma_control_mgr, m->wlr_output);
m->gamma_lut_changed = 0;
-
- if (!wlr_gamma_control_v1_apply(gamma_control, &pending))
- goto commit;
+ gamma_control = wlr_gamma_control_manager_v1_get_control(
+ gamma_control_mgr, m->wlr_output);
+ if (!wlr_gamma_control_v1_apply(gamma_control, &pending)) {
+ wlr_output_state_finish(&pending);
+ goto skip;
+ }
if (!wlr_output_test_state(m->wlr_output, &pending)) {
wlr_gamma_control_v1_send_failed_and_destroy(gamma_control);
- goto commit;
+ wlr_output_state_set_gamma_lut(&pending, 0, NULL, NULL, NULL);
}
- wlr_output_commit_state(m->wlr_output, &pending);
- wlr_output_schedule_frame(m->wlr_output);
- } else {
-commit:
- wlr_scene_output_commit(m->scene_output, NULL);
}
-skip:
- /* Let clients know a frame has been rendered */
- clock_gettime(CLOCK_MONOTONIC, &now);
- wlr_scene_output_send_frame_done(m->scene_output, &now);
+ if (outputcantear(m)) {
+ pending.tearing_page_flip = true;
+
+ if (!wlr_output_test_state(m->wlr_output, &pending)) {
+ fprintf(stderr, "Output test failed on '%s', retrying without tearing page-flip\n", m->wlr_output->name);
+
+ pending.tearing_page_flip = false;
+ }
+ }
+
+ if (!wlr_output_commit_state(m->wlr_output, &pending))
+ fprintf(stderr, "Page-flip failed on output %s", m->wlr_output->name);
+
wlr_output_state_finish(&pending);
+
+skip:
+ /* Send frame done to all visible surfaces */
+ clock_gettime(CLOCK_MONOTONIC, &frame_done_data.when);
+ frame_done_data.mon = m;
+ wlr_scene_output_for_each_buffer(m->scene_output, sendframedoneiterator, &frame_done_data);
}
void
@@ -2271,6 +2405,16 @@ run(char *startup_cmd)
wl_display_run(dpy);
}
+void
+sendframedoneiterator(struct wlr_scene_buffer *buffer, int x, int y, void *user_data)
+{
+ SendFrameDoneData *data = user_data;
+ if (buffer->primary_output != data->mon->scene_output)
+ return;
+
+ wlr_scene_buffer_send_frame_done(buffer, &data->when);
+}
+
void
setcursor(struct wl_listener *listener, void *data)
{
@@ -2630,6 +2774,11 @@ setup(void)
LISTEN_STATIC(&output_mgr->events.apply, outputmgrapply);
LISTEN_STATIC(&output_mgr->events.test, outputmgrtest);
+ tearing_control_v1 = wlr_tearing_control_manager_v1_create(dpy, 1);
+ tearing_control_new_object.notify = handlenewtearinghint;
+ wl_signal_add(&tearing_control_v1->events.new_object, &tearing_control_new_object);
+ wl_list_init(&tearing_controllers);
+
/* Make sure XWayland clients don't connect to the parent X server,
* e.g when running in the x11 backend or the wayland backend and the
* compositor has Xwayland support */
--
2.45.2