summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBerke Güzel <wenekar1@gmail.com>2026-10-12 14:27:51 +0300
committerBerke Güzel <wenekar1@gmail.com>2026-10-12 14:27:51 +0300
commit9ed89c17dee752154c36d71cae93d20cccfc71ef (patch)
treea32cd894643ff6f6ebce83dd745bd24b35eb6520 /src
parentaedd863e6f66fd446227e9b107bc9c1970092634 (diff)
perf: optimize hot-path loops, data structures, and cache locality
Diffstat (limited to 'src')
-rw-r--r--src/common/backend.c8
-rw-r--r--src/common/input.c9
-rw-r--r--src/common/loop.c54
-rw-r--r--src/wayland/buffer.c34
-rw-r--r--src/wayland/clipboard.c25
-rw-r--r--src/wayland/internal.h9
-rw-r--r--src/wayland/pointer.c66
-rw-r--r--src/wayland/popup.c5
-rw-r--r--src/wayland/surface.c12
-rw-r--r--src/x11/window.c28
10 files changed, 162 insertions, 88 deletions
diff --git a/src/common/backend.c b/src/common/backend.c
index 4c755bd..c8d2caf 100644
--- a/src/common/backend.c
+++ b/src/common/backend.c
@@ -217,9 +217,13 @@ bool yasl_surface_present(struct yasl_surface *s, const struct yasl_framebuffer
fb->size.x <= 0 || fb->size.y <= 0 ||
damage->n < -1 || damage->n > YASL_DAMAGE_MAX)
return false;
- struct yasl_damage_list clipped = {.n = damage->n};
- if (damage->n >= 0) {
+ struct yasl_damage_list clipped;
+ clipped.n = damage->n;
+ if (damage->n < 0) {
+ clipped.bounds = damage->bounds;
+ } else {
clipped.n = 0;
+ clipped.bounds = (struct yasl_rect2i){0};
for (int i = 0; i < damage->n; i++) {
struct yasl_rect2i r = damage->rects[i];
if (r.width < 0 || r.height < 0) return false;
diff --git a/src/common/input.c b/src/common/input.c
index eac4d1a..ff578e3 100644
--- a/src/common/input.c
+++ b/src/common/input.c
@@ -53,8 +53,13 @@ bool input_queue_push(struct input_queue *q, const struct yasl_input_event *ev)
goto failed;
struct yasl_input_event *events = malloc(capacity * sizeof(*events));
if (!events) goto failed;
- for (size_t i = 0; i < q->count; i++)
- events[i] = q->events[(q->head + i) % q->capacity];
+ if (q->events && q->count > 0) {
+ size_t first = q->capacity - q->head;
+ if (first > q->count) first = q->count;
+ memcpy(events, q->events + q->head, first * sizeof(*events));
+ if (q->count > first)
+ memcpy(events + first, q->events, (q->count - first) * sizeof(*events));
+ }
free(q->events);
q->events = events;
q->capacity = capacity;
diff --git a/src/common/loop.c b/src/common/loop.c
index a63df16..18d62b1 100644
--- a/src/common/loop.c
+++ b/src/common/loop.c
@@ -25,9 +25,9 @@ struct yasl_fd_source {
struct yasl_timer_source {
yasl_timer_cb fn;
void *ud;
- yasl_timer_id id;
uint64_t interval_ns;
uint64_t deadline_ns;
+ yasl_timer_id id;
bool deleted;
};
@@ -330,13 +330,18 @@ int yasl_loop_step(struct yasl_loop *l, int timeout_ms)
for (int i = 0; i < count; i++) {
if (pcopy[i].revents) {
short revents = pcopy[i].revents;
- // Verify that the source was not deleted by an earlier callback in this step
+ // Fast-path: check if source is still at index i before linear scan
bool valid = false;
- for (int j = 0; j < l->count; j++) {
- if (l->fds[j].id == scopy[i].id) {
- l->pollfds[j].revents = 0;
- valid = true;
- break;
+ if (i < l->count && l->fds[i].id == scopy[i].id) {
+ l->pollfds[i].revents = 0;
+ valid = true;
+ } else {
+ for (int j = 0; j < l->count; j++) {
+ if (l->fds[j].id == scopy[i].id) {
+ l->pollfds[j].revents = 0;
+ valid = true;
+ break;
+ }
}
}
if (valid && scopy[i].fn) {
@@ -367,18 +372,31 @@ int yasl_loop_step(struct yasl_loop *l, int timeout_ms)
uint64_t next_interval = timer_src.fn(ud, tid, interval);
handled++;
- for (int j = 0; j < l->timer_count; j++) {
- if (l->timers[j].id == tid) {
- if (next_interval == 0 || l->timers[j].deleted) {
- l->timers[j].deleted = true;
- } else {
- l->timers[j].interval_ns = next_interval;
- if (l->timers[j].deadline_ns + next_interval > now)
- l->timers[j].deadline_ns += next_interval;
- else
- l->timers[j].deadline_ns = now + next_interval;
+ // Fast-path: timer is usually still at index i unless an earlier timer was deleted
+ if (i < l->timer_count && l->timers[i].id == tid) {
+ if (next_interval == 0 || l->timers[i].deleted) {
+ l->timers[i].deleted = true;
+ } else {
+ l->timers[i].interval_ns = next_interval;
+ if (l->timers[i].deadline_ns + next_interval > now)
+ l->timers[i].deadline_ns += next_interval;
+ else
+ l->timers[i].deadline_ns = now + next_interval;
+ }
+ } else {
+ for (int j = 0; j < l->timer_count; j++) {
+ if (l->timers[j].id == tid) {
+ if (next_interval == 0 || l->timers[j].deleted) {
+ l->timers[j].deleted = true;
+ } else {
+ l->timers[j].interval_ns = next_interval;
+ if (l->timers[j].deadline_ns + next_interval > now)
+ l->timers[j].deadline_ns += next_interval;
+ else
+ l->timers[j].deadline_ns = now + next_interval;
+ }
+ break;
}
- break;
}
}
}
diff --git a/src/wayland/buffer.c b/src/wayland/buffer.c
index a1486fa..0561da3 100644
--- a/src/wayland/buffer.c
+++ b/src/wayland/buffer.c
@@ -322,19 +322,16 @@ int buffer_pick_free_index(const struct buffer *bufs, int cur)
return free_buf_lut[(unsigned)cur & 3][busy];
}
-struct latency_feedback {
- struct wayland_surface *window;
- struct wp_presentation_feedback *proxy;
- struct latency_feedback *next;
-};
-
static void feedback_destroy(struct latency_feedback *feedback)
{
struct latency_feedback **link = &feedback->window->latency_feedbacks;
while (*link && *link != feedback) link = &(*link)->next;
if (*link) *link = feedback->next;
wp_presentation_feedback_destroy(feedback->proxy);
- free(feedback);
+ if (feedback->in_pool)
+ *feedback = (struct latency_feedback){0};
+ else
+ free(feedback);
}
void surface_feedback_destroy(struct wayland_surface *w)
@@ -386,10 +383,22 @@ void surface_request_presentation_feedback(struct wayland_surface *w)
return;
struct wp_presentation_feedback *fb =
wp_presentation_feedback(w->conn->presentation, w->surface);
- struct latency_feedback *feedback = fb ? calloc(1, sizeof(*feedback)) : NULL;
- if (!feedback) {
- if (fb) wp_presentation_feedback_destroy(fb);
+ if (!fb)
return;
+ struct latency_feedback *feedback = NULL;
+ for (int i = 0; i < 8; i++) {
+ if (!w->latency_pool[i].window) {
+ feedback = &w->latency_pool[i];
+ feedback->in_pool = true;
+ break;
+ }
+ }
+ if (!feedback) {
+ feedback = calloc(1, sizeof(*feedback));
+ if (!feedback) {
+ wp_presentation_feedback_destroy(fb);
+ return;
+ }
}
feedback->window = w;
feedback->proxy = fb;
@@ -449,7 +458,10 @@ bool wayland_surface_present(struct wayland_surface *w, const struct yasl_frameb
fb->size.x != w->width || fb->size.y != w->height)
return false;
- w->damage = *damage;
+ w->damage.n = damage->n;
+ w->damage.bounds = damage->bounds;
+ if (damage->n > 0)
+ memcpy(w->damage.rects, damage->rects, (size_t)damage->n * sizeof(damage->rects[0]));
arm_frame(w);
if (w->closed)
return false;
diff --git a/src/wayland/clipboard.c b/src/wayland/clipboard.c
index 7df1a70..e3eec0f 100644
--- a/src/wayland/clipboard.c
+++ b/src/wayland/clipboard.c
@@ -118,21 +118,18 @@ static int transfer_io(int fd, short events, void *ud)
if (t->offset == t->len || n == 0 ||
(n < 0 && errno != EAGAIN && errno != EINTR)) transfer_destroy(t);
} else {
- char buf[8192];
- ssize_t n = read(fd, buf, sizeof(buf));
+ if (t->capacity - t->len < 4096) {
+ size_t capacity = t->capacity ? t->capacity * 2 : 8192;
+ if (capacity > CLIPBOARD_TEXT_MAX) capacity = CLIPBOARD_TEXT_MAX;
+ if (capacity <= t->len) { transfer_destroy(t); return 0; }
+ char *text = realloc(t->text, capacity);
+ if (!text) { transfer_destroy(t); return 0; }
+ t->text = text;
+ t->capacity = capacity;
+ }
+ ssize_t n = read(fd, t->text + t->len, t->capacity - t->len);
if (n > 0) {
- size_t need = t->len + (size_t)n;
- if (need > CLIPBOARD_TEXT_MAX) { transfer_destroy(t); return 0; }
- if (need > t->capacity) {
- size_t capacity = t->capacity ? t->capacity * 2 : sizeof(buf);
- if (capacity > CLIPBOARD_TEXT_MAX) capacity = CLIPBOARD_TEXT_MAX;
- char *text = realloc(t->text, capacity);
- if (!text) { transfer_destroy(t); return 0; }
- t->text = text;
- t->capacity = capacity;
- }
- memcpy(t->text + t->len, buf, (size_t)n);
- t->len += n;
+ t->len += (size_t)n;
} else if (n == 0) {
t->owner->receive(t->text ? t->text : "", t->len, t->uri_list,
t->drop_surface, t->owner->ud);
diff --git a/src/wayland/internal.h b/src/wayland/internal.h
index 04142f7..d519abd 100644
--- a/src/wayland/internal.h
+++ b/src/wayland/internal.h
@@ -76,6 +76,7 @@ struct pending_press {
struct press_queue {
struct pending_press ev[8];
+ int head;
int n;
};
@@ -244,6 +245,13 @@ struct wayland_display {
bool lost;
};
+struct latency_feedback {
+ struct wayland_surface *window;
+ struct wp_presentation_feedback *proxy;
+ struct latency_feedback *next;
+ bool in_pool;
+};
+
struct wayland_surface {
struct yasl_surface base;
/* 8-byte pointers & 64-bit values */
@@ -264,6 +272,7 @@ struct wayland_surface {
struct seat *ime_seat;
struct wp_tearing_control_v1 *tearing_control;
struct latency_feedback *latency_feedbacks;
+ struct latency_feedback latency_pool[8];
struct wp_fifo_v1 *fifo;
struct wp_commit_timer_v1 *commit_timer;
struct wl_callback *frame_cb;
diff --git a/src/wayland/pointer.c b/src/wayland/pointer.c
index 5924eb1..065669f 100644
--- a/src/wayland/pointer.c
+++ b/src/wayland/pointer.c
@@ -248,8 +248,9 @@ static void press_queue_flush(struct seat *s, struct press_queue *q,
struct yasl_mouse_event like)
{
int kept = 0;
- for (int i = 0; i < q->n; i++) {
- struct pending_press p = q->ev[i];
+ int count = q->n;
+ for (int i = 0; i < count; i++) {
+ struct pending_press p = q->ev[(q->head + i) & 7];
if (only && p.surface != only) {
q->ev[kept++] = p;
continue;
@@ -263,26 +264,30 @@ static void press_queue_flush(struct seat *s, struct press_queue *q,
ev.kind = (enum yasl_pointer_kind)p.release;
push_mouse(s, p.surface, ev);
}
+ q->head = 0;
q->n = kept;
}
static void press_queue_push(struct press_queue *q, struct wayland_surface *w,
struct yasl_vector2i pos, int button, bool release)
{
- if (q->n == (int)(sizeof(q->ev) / sizeof(q->ev[0]))) {
- memmove(q->ev, q->ev + 1, sizeof(q->ev[0]) * (size_t)(q->n - 1));
+ if (q->n == 8) {
+ q->head = (q->head + 1) & 7;
q->n--;
}
- q->ev[q->n++] = (struct pending_press){
+ q->ev[(q->head + q->n) & 7] = (struct pending_press){
.surface = w, .pos = pos, .button = button, .release = release,
};
+ q->n++;
}
static void press_queue_forget(struct press_queue *q, struct wayland_surface *w)
{
- for (int i = 0; i < q->n; i++)
- if (q->ev[i].surface == w)
- q->ev[i].surface = NULL;
+ for (int i = 0; i < q->n; i++) {
+ int idx = (q->head + i) & 7;
+ if (q->ev[idx].surface == w)
+ q->ev[idx].surface = NULL;
+ }
}
void seat_pointer_reset(struct seat *s)
@@ -566,28 +571,7 @@ static struct yasl_vector2i touch_pos(const struct touch_point *p)
};
}
-static int touch_count(const struct seat *s)
-{
- int n = 0;
- for (int i = 0; i < SEAT_TOUCH_POINTS; i++)
- if (s->touch_points[i].active && !s->touch_points[i].up)
- n++;
- return n;
-}
-static double touch_centroid_y(const struct seat *s, const struct wayland_surface *on)
-{
- double sum = 0;
- int n = 0;
- for (int i = 0; i < SEAT_TOUCH_POINTS; i++) {
- const struct touch_point *p = &s->touch_points[i];
- if (p->active && !p->up && p->surface == on) {
- sum += p->y;
- n++;
- }
- }
- return n ? sum / n : 0;
-}
static void touch_report(struct seat *s, struct touch_point *p, int button,
enum yasl_pointer_kind kind)
@@ -641,24 +625,35 @@ static void touch_frame(void *data, struct wl_touch *touch)
{
(void)touch;
struct seat *s = data;
+ int active_n = 0;
+ double centroid_sum = 0;
+ int centroid_n = 0;
for (int i = 0; i < SEAT_TOUCH_POINTS; i++) {
struct touch_point *p = &s->touch_points[i];
- if (p->active && p->down && !s->touch_primary && !s->touch_scrolling) {
+ if (!p->active) continue;
+ if (p->down && !s->touch_primary && !s->touch_scrolling) {
s->touch_primary = p;
s->touch_pressed = true;
touch_report(s, p, 0, YASL_PTR_PRESS);
}
+ if (!p->up) {
+ active_n++;
+ if (s->touch_primary && p->surface == s->touch_primary->surface) {
+ centroid_sum += p->y;
+ centroid_n++;
+ }
+ }
}
struct touch_point *primary = s->touch_primary;
- if (primary && !s->touch_scrolling && touch_count(s) >= 2) {
+ if (primary && !s->touch_scrolling && active_n >= 2) {
if (s->touch_pressed)
touch_report(s, primary, 0, YASL_PTR_RELEASE);
s->touch_pressed = false;
s->touch_scrolling = true;
- s->touch_scroll_y = touch_centroid_y(s, primary->surface);
+ s->touch_scroll_y = centroid_n ? centroid_sum / centroid_n : 0;
s->touch_scroll_rest = 0;
- } else if (s->touch_scrolling && primary && touch_count(s) >= 2) {
- double y = touch_centroid_y(s, primary->surface);
+ } else if (s->touch_scrolling && primary && active_n >= 2) {
+ double y = centroid_n ? centroid_sum / centroid_n : 0;
double dy = y - s->touch_scroll_y;
s->touch_scroll_y = y;
if (dy != 0.0) {
@@ -679,6 +674,7 @@ static void touch_frame(void *data, struct wl_touch *touch)
struct touch_point *p = &s->touch_points[i];
if (!p->active) continue;
if (p->up) {
+ active_n--;
if (p == s->touch_primary) {
if (s->touch_pressed)
touch_report(s, p, 0, YASL_PTR_RELEASE);
@@ -690,7 +686,7 @@ static void touch_frame(void *data, struct wl_touch *touch)
p->down = p->moved = false;
}
}
- if (!touch_count(s))
+ if (active_n <= 0)
s->touch_scrolling = false;
}
diff --git a/src/wayland/popup.c b/src/wayland/popup.c
index 63bcba6..4364934 100644
--- a/src/wayland/popup.c
+++ b/src/wayland/popup.c
@@ -145,7 +145,10 @@ struct wayland_surface *wayland_surface_create_popup(struct wayland_surface *par
w->clock.refresh_ns = parent->clock.refresh_ns;
w->surface = wl_compositor_create_surface(c->compositor);
- if (!w->surface || surface_init_scaling(w) < 0)
+ if (!w->surface)
+ goto err;
+ wl_surface_set_user_data(w->surface, w);
+ if (surface_init_scaling(w) < 0)
goto err;
w->xdg_surface = xdg_wm_base_get_xdg_surface(c->wm_base, w->surface);
if (!w->xdg_surface)
diff --git a/src/wayland/surface.c b/src/wayland/surface.c
index ce1764b..242531c 100644
--- a/src/wayland/surface.c
+++ b/src/wayland/surface.c
@@ -321,7 +321,11 @@ static void conn_unlink(struct wayland_surface *w)
struct wayland_surface *conn_surface_for(struct wayland_display *c, struct wl_surface *s)
{
- for (struct wayland_surface *w = c->surfaces; w && s; w = w->conn_next)
+ if (unlikely(!c || !s)) return NULL;
+ struct wayland_surface *fast = (struct wayland_surface *)wl_surface_get_user_data(s);
+ if (likely(fast && fast->conn == c && fast->surface == s))
+ return fast;
+ for (struct wayland_surface *w = c->surfaces; w; w = w->conn_next)
if (w->surface == s)
return w;
return NULL;
@@ -361,6 +365,7 @@ struct wayland_surface *wayland_surface_create_toplevel(struct wayland_display *
w->surface = wl_compositor_create_surface(c->compositor);
if (!w->surface) goto err;
+ wl_surface_set_user_data(w->surface, w);
if (surface_init_scaling(w) < 0) goto err;
if (surface_init_shell(w, spec->title) < 0) goto err;
w->opaque = spec->fullscreen;
@@ -420,7 +425,10 @@ void wayland_surface_destroy(struct wayland_surface *w)
if (w->toplevel) xdg_toplevel_destroy(w->toplevel);
if (w->popup) xdg_popup_destroy(w->popup);
if (w->xdg_surface) xdg_surface_destroy(w->xdg_surface);
- if (w->surface) wl_surface_destroy(w->surface);
+ if (w->surface) {
+ wl_surface_set_user_data(w->surface, NULL);
+ wl_surface_destroy(w->surface);
+ }
struct wayland_display *c = w->conn;
conn_unlink(w);
static const struct yasl_surface_cbs no_callbacks = {0};
diff --git a/src/x11/window.c b/src/x11/window.c
index 5e1b612..271c128 100644
--- a/src/x11/window.c
+++ b/src/x11/window.c
@@ -1280,10 +1280,32 @@ static bool present(struct yasl_surface *base, const struct yasl_framebuffer *fb
XImage *im = s->image;
bool direct = im->bits_per_pixel == 32 && im->red_mask == 0xff0000 &&
im->green_mask == 0xff00 && im->blue_mask == 0xff;
- for (int y = 0; y < s->size.y; y++) {
+ int y_start = 0, y_end = s->size.y;
+ int x_start = 0, x_end = s->size.x;
+ if (damage->n >= 0) {
+ y_start = s->size.y; y_end = 0;
+ x_start = s->size.x; x_end = 0;
+ for (int i = 0; i < damage->n; i++) {
+ struct yasl_rect2i r = damage->rects[i];
+ int y0 = r.y > 0 ? r.y : 0;
+ int y1 = (r.y + r.height < s->size.y) ? r.y + r.height : s->size.y;
+ int x0 = r.x > 0 ? r.x : 0;
+ int x1 = (r.x + r.width < s->size.x) ? r.x + r.width : s->size.x;
+ if (y0 < y_start) y_start = y0;
+ if (y1 > y_end) y_end = y1;
+ if (x0 < x_start) x_start = x0;
+ if (x1 > x_end) x_end = x1;
+ }
+ if (y_start >= y_end || x_start >= x_end) {
+ y_start = y_end = x_start = x_end = 0;
+ }
+ }
+ size_t copy_bytes = (size_t)(x_end - x_start) * 4;
+ for (int y = y_start; y < y_end; y++) {
const uint32_t *row = s->pixels + (size_t)y * s->size.x;
- if (direct) memcpy(im->data + (size_t)y * im->bytes_per_line, row, (size_t)s->size.x * 4);
- else for (int x = 0; x < s->size.x; x++) {
+ if (direct) memcpy(im->data + (size_t)y * im->bytes_per_line + (size_t)x_start * 4,
+ row + x_start, copy_bytes);
+ else for (int x = x_start; x < x_end; x++) {
uint32_t p = row[x];
XPutPixel(im, x, y, component((p >> 16) & 255, im->red_mask) |
component((p >> 8) & 255, im->green_mask) | component(p & 255, im->blue_mask));