Compare commits

...

4 Commits

Author SHA1 Message Date
Maarten van Gompel
3826cd6050 version bump 2025-04-17 22:02:29 +02:00
Willow Barraco
73caeee513 Deduplicate some drwbuf fields
We have those fields on the drwbuf struct, but we always use the
same ones. Let's deduplicate those fields by moving them on the drwsurf.

Signed-off-by: Maarten van Gompel <proycon@anaproy.nl>
2025-04-17 19:48:53 +02:00
Willow Barraco
55b88bc14f Optimize the backporting condition
At the moment we flip the two buffers, and we backport the damaged
area on every frame callback. We completely ignore the compositor
sent event buffer->release. This event means that the compositor
finished reading the buffer pixels, and will never read them again. So
we can keep using the same buffer instead of fliping, if we receive the
event before asking for a new frame callback.

The change is not complicated, but we have to distinguish
drwsurf_attach, and drwsurf_flip. We try to flip before trying to
draw anything. And if the buffer has already been released, we don't
flip.

We also have to track the backport_damage separately than the buffer
current damage.

Signed-off-by: Maarten van Gompel <proycon@anaproy.nl>
2025-04-17 19:48:50 +02:00
Willow Barraco
686b27a72d Implement double buffering
Until this point we was using one single buffer. This cause tearing when
they are scanned while we are drawing.

This refactorize our buffer and damage tracking to use two buffers. One
buffer, the back_buffer, is our dirty area. The other one, the
display_buffer, is left untouched.

Now we only flip buffers on adequate moments, indicated by the
compositor sending the frame callback event. We can draw multiple
time between those events, and we store all damaged areas. We only
schedule frame callbacks when needed, after storing a new damaged area.

Once flipped, we backport the damaged area from the new display_buffer
to the new back_buffer. Which generally means, for wvkbd, one rectangle
for each one of the surf and popup_surf.

Signed-off-by: Maarten van Gompel <proycon@anaproy.nl>
2025-04-17 19:48:43 +02:00
5 changed files with 184 additions and 58 deletions

View File

@ -1,4 +1,4 @@
VERSION = 0.16.1
VERSION = 0.17
CFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=700
PREFIX = /usr/local
MANPREFIX = ${PREFIX}/share/man

170
drw.c
View File

@ -6,34 +6,131 @@
#include "shm_open.h"
#include "math.h"
void drwbuf_handle_release(void *data, struct wl_buffer *wl_buffer) {
struct drwsurf *ds = data;
ds->released = true;
};
const struct wl_buffer_listener buffer_listener = {
.release = drwbuf_handle_release
};
void drwsurf_handle_frame_cb(void* data, struct wl_callback* callback,
uint32_t time)
{
struct drwsurf *ds = data;
wl_callback_destroy(ds->frame_cb);
ds->frame_cb = NULL;
cairo_rectangle_int_t r = {0};
for (int i = 0; i < cairo_region_num_rectangles(ds->damage); i++) {
cairo_region_get_rectangle(ds->damage, i, &r);
wl_surface_damage(ds->surf, r.x, r.y, r.width, r.height);
};
cairo_region_subtract(ds->damage, ds->damage);
drwsurf_attach(ds);
}
const struct wl_callback_listener frame_listener = {
.done = drwsurf_handle_frame_cb
};
void drwsurf_register_frame_cb(struct drwsurf *ds)
{
if (ds->frame_cb)
return;
if (!ds->attached)
return;
ds->frame_cb = wl_surface_frame(ds->surf);
wl_callback_add_listener(ds->frame_cb, &frame_listener, ds);
wl_surface_commit(ds->surf);
}
void drwsurf_damage(struct drwsurf *ds, uint32_t x, uint32_t y, uint32_t w, uint32_t h)
{
cairo_rectangle_int_t rect = { x, y, w, h };
cairo_region_union_rectangle(ds->damage, &rect);
cairo_region_union_rectangle(ds->backport_damage, &rect);
drwsurf_register_frame_cb(ds);
}
void
drwsurf_resize(struct drwsurf *ds, uint32_t w, uint32_t h, double s)
{
if (ds->buf) {
munmap(ds->pool_data, ds->size);
wl_buffer_destroy(ds->buf);
ds->buf = NULL;
}
ds->scale = s;
ds->width = ceil(w * s);
ds->height = ceil(h * s);
setup_buffer(ds);
if (ds->damage)
cairo_region_destroy(ds->damage);
ds->damage = cairo_region_create();
if (ds->backport_damage)
cairo_region_destroy(ds->backport_damage);
ds->backport_damage = cairo_region_create();
ds->released = true;
setup_buffer(ds, ds->back_buffer);
setup_buffer(ds, ds->display_buffer);
}
void
drwsurf_backport(struct drwsurf *ds)
{
cairo_save(ds->back_buffer->cairo);
cairo_scale(ds->back_buffer->cairo, 1/ds->scale, 1/ds->scale);
cairo_set_operator(ds->back_buffer->cairo, CAIRO_OPERATOR_SOURCE);
cairo_rectangle_int_t r = {0};
for (int i = 0; i < cairo_region_num_rectangles(ds->backport_damage); i++) {
cairo_region_get_rectangle(ds->backport_damage, i, &r);
cairo_set_source_surface(ds->back_buffer->cairo, ds->display_buffer->cairo_surf, 0, 0);
cairo_rectangle(
ds->back_buffer->cairo,
r.x * ds->scale,
r.y * ds->scale,
r.width * ds->scale,
r.height * ds->scale
);
cairo_fill(ds->back_buffer->cairo);
};
cairo_restore(ds->back_buffer->cairo);
cairo_region_subtract(ds->backport_damage, ds->backport_damage);
}
void
drwsurf_attach(struct drwsurf *ds)
{
wl_surface_attach(ds->surf, ds->back_buffer->buf, 0, 0);
wl_surface_commit(ds->surf);
ds->released = false;
ds->attached = true;
}
void
drwsurf_flip(struct drwsurf *ds)
{
wl_surface_attach(ds->surf, ds->buf, 0, 0);
wl_surface_commit(ds->surf);
if (ds->released)
return;
ds->released = true;
struct drwbuf *tmp = ds->back_buffer;
ds->back_buffer = ds->display_buffer;
ds->display_buffer = tmp;
drwsurf_backport(ds);
}
void
drw_draw_text(struct drwsurf *d, Color color, uint32_t x, uint32_t y,
drw_draw_text(struct drwsurf *ds, Color color, uint32_t x, uint32_t y,
uint32_t w, uint32_t h, uint32_t b, const char *label,
PangoFontDescription *font_description)
{
drwsurf_flip(ds);
struct drwbuf *d = ds->back_buffer;
cairo_save(d->cairo);
@ -58,8 +155,11 @@ drw_draw_text(struct drwsurf *d, Color color, uint32_t x, uint32_t y,
}
void
drw_do_clear(struct drwsurf *d, uint32_t x, uint32_t y, uint32_t w, uint32_t h)
drw_do_clear(struct drwsurf *ds, uint32_t x, uint32_t y, uint32_t w, uint32_t h)
{
drwsurf_flip(ds);
struct drwbuf *d = ds->back_buffer;
cairo_save(d->cairo);
cairo_set_operator(d->cairo, CAIRO_OPERATOR_CLEAR);
@ -70,9 +170,12 @@ drw_do_clear(struct drwsurf *d, uint32_t x, uint32_t y, uint32_t w, uint32_t h)
}
void
drw_do_rectangle(struct drwsurf *d, Color color, uint32_t x, uint32_t y,
drw_do_rectangle(struct drwsurf *ds, Color color, uint32_t x, uint32_t y,
uint32_t w, uint32_t h, bool over, int rounding)
{
drwsurf_flip(ds);
struct drwbuf *d = ds->back_buffer;
cairo_save(d->cairo);
if (over) {
@ -128,41 +231,52 @@ drw_over_rectangle(struct drwsurf *d, Color color, uint32_t x, uint32_t y,
}
uint32_t
setup_buffer(struct drwsurf *drwsurf)
setup_buffer(struct drwsurf *drwsurf, struct drwbuf *drwbuf)
{
int prev_size = drwbuf->size;
int stride = drwsurf->width * 4;
drwsurf->size = stride * drwsurf->height;
drwbuf->size = stride * drwsurf->height;
int fd = allocate_shm_file(drwsurf->size);
int fd = allocate_shm_file(drwbuf->size);
if (fd == -1) {
return 1;
}
drwsurf->pool_data =
mmap(NULL, drwsurf->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (drwsurf->pool_data == MAP_FAILED) {
if (drwbuf->pool_data)
munmap(drwbuf->pool_data, prev_size);
drwbuf->pool_data =
mmap(NULL, drwbuf->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (drwbuf->pool_data == MAP_FAILED) {
close(fd);
return 1;
}
if (drwbuf->buf)
wl_buffer_destroy(drwbuf->buf);
struct wl_shm_pool *pool =
wl_shm_create_pool(drwsurf->ctx->shm, fd, drwsurf->size);
drwsurf->buf =
wl_shm_create_pool(drwsurf->ctx->shm, fd, drwbuf->size);
drwbuf->buf =
wl_shm_pool_create_buffer(pool, 0, drwsurf->width, drwsurf->height,
stride, WL_SHM_FORMAT_ARGB8888);
wl_shm_pool_destroy(pool);
close(fd);
wl_buffer_add_listener(drwbuf->buf, &buffer_listener, drwsurf);
cairo_surface_t *s = cairo_image_surface_create_for_data(
drwsurf->pool_data, CAIRO_FORMAT_ARGB32, drwsurf->width,
if (drwbuf->cairo_surf)
cairo_surface_destroy(drwbuf->cairo_surf);
drwbuf->cairo_surf = cairo_image_surface_create_for_data(
drwbuf->pool_data, CAIRO_FORMAT_ARGB32, drwsurf->width,
drwsurf->height, stride);
drwsurf->cairo = cairo_create(s);
cairo_scale(drwsurf->cairo, drwsurf->scale, drwsurf->scale);
cairo_set_antialias(drwsurf->cairo, CAIRO_ANTIALIAS_NONE);
drwsurf->layout = pango_cairo_create_layout(drwsurf->cairo);
pango_layout_set_auto_dir(drwsurf->layout, false);
cairo_save(drwsurf->cairo);
if (drwbuf->cairo)
cairo_destroy(drwbuf->cairo);
drwbuf->cairo = cairo_create(drwbuf->cairo_surf);
cairo_scale(drwbuf->cairo, drwsurf->scale, drwsurf->scale);
cairo_set_antialias(drwbuf->cairo, CAIRO_ANTIALIAS_NONE);
drwbuf->layout = pango_cairo_create_layout(drwbuf->cairo);
pango_layout_set_auto_dir(drwbuf->layout, false);
cairo_save(drwbuf->cairo);
return 0;
}

35
drw.h
View File

@ -7,22 +7,35 @@
struct drw {
struct wl_shm *shm;
};
struct drwbuf {
uint32_t size;
struct wl_buffer *buf;
cairo_surface_t *cairo_surf;
cairo_t *cairo;
PangoLayout *layout;
unsigned char *pool_data;
};
struct drwsurf {
uint32_t width, height, size;
uint32_t width, height;
double scale;
struct drw *ctx;
struct wl_surface *surf;
struct wl_buffer *buf;
struct wl_shm *shm;
unsigned char *pool_data;
struct wl_callback *frame_cb;
cairo_t *cairo;
PangoLayout *layout;
cairo_region_t *damage, *backport_damage;
bool attached;
bool released;
struct drwbuf *back_buffer;
struct drwbuf *display_buffer;
};
struct kbd;
void drwsurf_damage(struct drwsurf *ds, uint32_t x, uint32_t y, uint32_t w, uint32_t h);
void drwsurf_resize(struct drwsurf *ds, uint32_t w, uint32_t h, double s);
void drwsurf_attach(struct drwsurf *ds);
void drwsurf_flip(struct drwsurf *ds);
typedef union {
@ -30,19 +43,19 @@ typedef union {
uint32_t color;
} Color;
void drw_do_clear(struct drwsurf *d, uint32_t x, uint32_t y,
void drw_do_clear(struct drwsurf *ds, uint32_t x, uint32_t y,
uint32_t w, uint32_t h);
void drw_do_rectangle(struct drwsurf *d, Color color, uint32_t x, uint32_t y,
void drw_do_rectangle(struct drwsurf *ds, Color color, uint32_t x, uint32_t y,
uint32_t w, uint32_t h, bool fill, int rounding);
void drw_fill_rectangle(struct drwsurf *d, Color color, uint32_t x, uint32_t y,
void drw_fill_rectangle(struct drwsurf *ds, Color color, uint32_t x, uint32_t y,
uint32_t w, uint32_t h, int rounding);
void drw_over_rectangle(struct drwsurf *d, Color color, uint32_t x, uint32_t y,
void drw_over_rectangle(struct drwsurf *ds, Color color, uint32_t x, uint32_t y,
uint32_t w, uint32_t h, int rounding);
void drw_draw_text(struct drwsurf *d, Color color, uint32_t x, uint32_t y,
void drw_draw_text(struct drwsurf *ds, Color color, uint32_t x, uint32_t y,
uint32_t w, uint32_t h, uint32_t b, const char *label,
PangoFontDescription *font_description);
uint32_t setup_buffer(struct drwsurf *drwsurf);
uint32_t setup_buffer(struct drwsurf *ds, struct drwbuf *db);
#endif

View File

@ -337,10 +337,7 @@ kbd_release_key(struct kbd *kb, uint32_t time)
kb->last_swipe = NULL;
}
drwsurf_flip(kb->surf);
kbd_clear_last_popup(kb);
drwsurf_flip(kb->popup_surf);
}
void
@ -366,10 +363,7 @@ kbd_motion_key(struct kbd *kb, uint32_t time, uint32_t x, uint32_t y)
kbd_unpress_key(kb, time);
}
drwsurf_flip(kb->surf);
kbd_clear_last_popup(kb);
drwsurf_flip(kb->popup_surf);
}
void
@ -501,9 +495,6 @@ kbd_press_key(struct kbd *kb, struct key *k, uint32_t time)
default:
break;
}
drwsurf_flip(kb->surf);
drwsurf_flip(kb->popup_surf);
}
void
@ -553,8 +544,13 @@ kbd_clear_last_popup(struct kbd *kb)
if (kb->last_popup_w && kb->last_popup_h) {
drw_do_clear(kb->popup_surf, kb->last_popup_x, kb->last_popup_y,
kb->last_popup_w, kb->last_popup_h);
wl_surface_damage(kb->popup_surf->surf, kb->last_popup_x,
kb->last_popup_y, kb->last_popup_w, kb->last_popup_h);
drwsurf_damage(
kb->popup_surf,
kb->last_popup_x,
kb->last_popup_y,
kb->last_popup_w,
kb->last_popup_h
);
kb->last_popup_w = kb->last_popup_h = 0;
}
@ -588,7 +584,7 @@ kbd_draw_key(struct kbd *kb, struct key *k, enum key_draw_type type)
drw_draw_text(kb->surf, scheme->text, k->x, k->y, k->w, k->h,
KBD_KEY_BORDER, label, scheme->font_description);
wl_surface_damage(kb->surf->surf, k->x, k->y, k->w, k->h);
drwsurf_damage(kb->surf, k->x, k->y, k->w, k->h);
if (type == Press || type == Unpress) {
kbd_clear_last_popup(kb);
@ -605,8 +601,7 @@ kbd_draw_key(struct kbd *kb, struct key *k, enum key_draw_type type)
drw_draw_text(kb->popup_surf, scheme->text, k->x, kb->last_popup_y,
k->w, k->h, KBD_KEY_BORDER, label,
scheme->font_description);
wl_surface_damage(kb->popup_surf->surf, k->x, kb->last_popup_y, k->w,
k->h);
drwsurf_damage(kb->popup_surf, k->x, kb->last_popup_y, k->w, k->h);
}
}
@ -633,7 +628,7 @@ kbd_draw_layout(struct kbd *kb)
}
next_key++;
}
wl_surface_damage(d->surf, 0, 0, kb->w, kb->h);
drwsurf_damage(d, 0, 0, kb->w, kb->h);
}
void

10
main.c
View File

@ -62,6 +62,7 @@ static int wl_outputs_size;
/* drawing */
static struct drw draw_ctx;
static struct drwbuf draw_surf_back_buffer, draw_surf_display_buffer, popup_draw_surf_back_buffer, popup_draw_surf_display_buffer;
static struct drwsurf draw_surf, popup_draw_surf;
/* layer surface parameters */
@ -332,7 +333,6 @@ wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time,
}
kbd_next_layer(&keyboard, NULL, (value >= 0));
drwsurf_flip(keyboard.surf);
}
void
@ -517,7 +517,7 @@ xdg_popup_surface_configure(void *data, struct xdg_surface *xdg_surface,
{
xdg_surface_ack_configure(xdg_surface, serial);
popup_xdg_surface_configured = true;
drwsurf_flip(&popup_draw_surf);
drwsurf_attach(&popup_draw_surf);
}
static const struct xdg_surface_listener xdg_popup_surface_listener = {
@ -664,7 +664,7 @@ layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface,
zwlr_layer_surface_v1_ack_configure(surface, serial);
kbd_resize(&keyboard, layouts, NumLayouts);
drwsurf_flip(&draw_surf);
drwsurf_attach(&draw_surf);
} else {
zwlr_layer_surface_v1_ack_configure(surface, serial);
}
@ -1018,7 +1018,11 @@ main(int argc, char **argv)
}
draw_surf.ctx = &draw_ctx;
draw_surf.back_buffer = &draw_surf_back_buffer;
draw_surf.display_buffer = &draw_surf_display_buffer;
popup_draw_surf.ctx = &draw_ctx;
popup_draw_surf.back_buffer = &popup_draw_surf_back_buffer;
popup_draw_surf.display_buffer = &popup_draw_surf_display_buffer;
keyboard.surf = &draw_surf;
keyboard.popup_surf = &popup_draw_surf;