chiark / gitweb /
terminal/grdev: allow arbitrary fb-age contexts
[elogind.git] / src / libsystemd-terminal / grdev-drm.c
index 5393ebf98880c0ae1338e22e32958602f251d411..f01df1dae269102da0965fac2eef3285b6b3dbb9 100644 (file)
@@ -1095,19 +1095,19 @@ static void grdrm_crtc_expose(grdrm_crtc *crtc) {
         grdev_pipe_ready(&crtc->pipe->base, true);
 }
 
-static void grdrm_crtc_commit_deep(grdrm_crtc *crtc, grdev_fb **slot) {
+static void grdrm_crtc_commit_deep(grdrm_crtc *crtc, grdev_fb *basefb) {
         struct drm_mode_crtc set_crtc = { .crtc_id = crtc->object.id };
         grdrm_card *card = crtc->object.card;
         grdrm_pipe *pipe = crtc->pipe;
-        grdrm_fb *fb = fb_from_base(*slot);
-        size_t i;
+        grdrm_fb *fb;
         int r;
 
         assert(crtc);
-        assert(slot);
-        assert(*slot);
+        assert(basefb);
         assert(pipe);
 
+        fb = fb_from_base(basefb);
+
         set_crtc.set_connectors_ptr = PTR_TO_UINT64(crtc->set.connectors);
         set_crtc.count_connectors = crtc->set.n_connectors;
         set_crtc.fb_id = fb->id;
@@ -1132,7 +1132,7 @@ static void grdrm_crtc_commit_deep(grdrm_crtc *crtc, grdev_fb **slot) {
                 crtc->applied = true;
         }
 
-        *slot = NULL;
+        pipe->base.back = NULL;
         pipe->base.front = &fb->base;
         fb->flipid = 0;
         ++pipe->counter;
@@ -1144,40 +1144,25 @@ static void grdrm_crtc_commit_deep(grdrm_crtc *crtc, grdev_fb **slot) {
          * To avoid duplicating that everywhere, we schedule our own
          * timer and raise a fake FRAME event when it fires. */
         grdev_pipe_schedule(&pipe->base, 1);
-
-        if (!pipe->base.back) {
-                for (i = 0; i < pipe->base.max_fbs; ++i) {
-                        if (!pipe->base.fbs[i])
-                                continue;
-
-                        fb = fb_from_base(pipe->base.fbs[i]);
-                        if (&fb->base == pipe->base.front)
-                                continue;
-
-                        fb->flipid = 0;
-                        pipe->base.back = &fb->base;
-                        break;
-                }
-        }
 }
 
-static int grdrm_crtc_commit_flip(grdrm_crtc *crtc, grdev_fb **slot) {
+static int grdrm_crtc_commit_flip(grdrm_crtc *crtc, grdev_fb *basefb) {
         struct drm_mode_crtc_page_flip page_flip = { .crtc_id = crtc->object.id };
         grdrm_card *card = crtc->object.card;
         grdrm_pipe *pipe = crtc->pipe;
-        grdrm_fb *fb = fb_from_base(*slot);
+        grdrm_fb *fb;
         uint32_t cnt;
-        size_t i;
         int r;
 
         assert(crtc);
-        assert(slot);
-        assert(*slot);
+        assert(basefb);
         assert(pipe);
 
         if (!crtc->applied && !grdrm_modes_compatible(&crtc->kern.mode, &crtc->set.mode))
                 return 0;
 
+        fb = fb_from_base(basefb);
+
         cnt = ++pipe->counter ? : ++pipe->counter;
         page_flip.fb_id = fb->id;
         page_flip.flags = DRM_MODE_PAGE_FLIP_EVENT;
@@ -1209,29 +1194,13 @@ static int grdrm_crtc_commit_flip(grdrm_crtc *crtc, grdev_fb **slot) {
         pipe->base.flip = false;
         pipe->counter = cnt;
         fb->flipid = cnt;
-        *slot = NULL;
+        pipe->base.back = NULL;
 
         /* Raise fake FRAME event if it takes longer than 2
          * frames to receive the pageflip event. We assume the
          * queue ran over or some other error happened. */
         grdev_pipe_schedule(&pipe->base, 2);
 
-        if (!pipe->base.back) {
-                for (i = 0; i < pipe->base.max_fbs; ++i) {
-                        if (!pipe->base.fbs[i])
-                                continue;
-
-                        fb = fb_from_base(pipe->base.fbs[i]);
-                        if (&fb->base == pipe->base.front)
-                                continue;
-                        if (fb->flipid)
-                                continue;
-
-                        pipe->base.back = &fb->base;
-                        break;
-                }
-        }
-
         return 1;
 }
 
@@ -1239,7 +1208,7 @@ static void grdrm_crtc_commit(grdrm_crtc *crtc) {
         struct drm_mode_crtc set_crtc = { .crtc_id = crtc->object.id };
         grdrm_card *card = crtc->object.card;
         grdrm_pipe *pipe;
-        grdev_fb **slot;
+        grdev_fb *fb;
         int r;
 
         assert(crtc);
@@ -1280,19 +1249,19 @@ static void grdrm_crtc_commit(grdrm_crtc *crtc) {
         assert(crtc->set.n_connectors > 0);
 
         if (pipe->base.flip)
-                slot = &pipe->base.back;
+                fb = pipe->base.back;
         else if (!crtc->applied)
-                slot = &pipe->base.front;
+                fb = pipe->base.front;
         else
                 return;
 
-        if (!*slot)
+        if (!fb)
                 return;
 
-        r = grdrm_crtc_commit_flip(crtc, slot);
+        r = grdrm_crtc_commit_flip(crtc, fb);
         if (r == 0) {
                 /* in case we couldn't page-flip, perform deep modeset */
-                grdrm_crtc_commit_deep(crtc, slot);
+                grdrm_crtc_commit_deep(crtc, fb);
         }
 }
 
@@ -1335,7 +1304,6 @@ static void grdrm_crtc_restore(grdrm_crtc *crtc) {
 static void grdrm_crtc_flip_complete(grdrm_crtc *crtc, uint32_t counter, struct drm_event_vblank *event) {
         bool flipped = false;
         grdrm_pipe *pipe;
-        grdrm_fb *back = NULL;
         size_t i;
 
         assert(crtc);
@@ -1362,20 +1330,13 @@ static void grdrm_crtc_flip_complete(grdrm_crtc *crtc, uint32_t counter, struct
                 fb = fb_from_base(pipe->base.fbs[i]);
                 if (counter != 0 && counter == pipe->counter && fb->flipid == counter) {
                         pipe->base.front = &fb->base;
+                        fb->flipid = 0;
                         flipped = true;
-                }
-
-                if (counter - fb->flipid < UINT16_MAX) {
+                } else if (counter - fb->flipid < UINT16_MAX) {
                         fb->flipid = 0;
-                        back = fb;
-                } else if (fb->flipid == 0) {
-                        back = fb;
                 }
         }
 
-        if (!pipe->base.back)
-                pipe->base.back = &back->base;
-
         if (flipped) {
                 crtc->pipe->base.flipping = false;
                 grdev_pipe_frame(&pipe->base);
@@ -1481,6 +1442,9 @@ grdrm_fb *grdrm_fb_free(grdrm_fb *fb) {
 
         assert(fb->card);
 
+        if (fb->base.free_fn)
+                fb->base.free_fn(fb->base.data.ptr);
+
         if (fb->id > 0 && fb->card->fd >= 0) {
                 r = ioctl(fb->card->fd, DRM_IOCTL_MODE_RMFB, fb->id);
                 if (r < 0)
@@ -1562,8 +1526,32 @@ static void grdrm_pipe_free(grdev_pipe *basepipe) {
         free(pipe);
 }
 
+static grdev_fb *grdrm_pipe_target(grdev_pipe *basepipe) {
+        grdrm_fb *fb;
+        size_t i;
+
+        if (!basepipe->back) {
+                for (i = 0; i < basepipe->max_fbs; ++i) {
+                        if (!basepipe->fbs[i])
+                                continue;
+
+                        fb = fb_from_base(basepipe->fbs[i]);
+                        if (&fb->base == basepipe->front)
+                                continue;
+                        if (basepipe->flipping && fb->flipid)
+                                continue;
+
+                        basepipe->back = &fb->base;
+                        break;
+                }
+        }
+
+        return basepipe->back;
+}
+
 static const grdev_pipe_vtable grdrm_pipe_vtable = {
         .free                   = grdrm_pipe_free,
+        .target                 = grdrm_pipe_target,
 };
 
 /*
@@ -2196,7 +2184,8 @@ static int grdrm_card_io_fn(sd_event_source *s, int fd, uint32_t revents, void *
         uint32_t id, counter;
         grdrm_object *object;
         char buf[4096];
-        ssize_t l, i;
+        size_t len;
+        ssize_t l;
 
         if (revents & (EPOLLHUP | EPOLLERR)) {
                 /* Immediately close device on HUP; no need to flush pending
@@ -2215,15 +2204,12 @@ static int grdrm_card_io_fn(sd_event_source *s, int fd, uint32_t revents, void *
                         log_debug("grdrm: %s/%s: read error: %m", card->base.session->name, card->base.name);
                         grdrm_card_close(card);
                         return 0;
-                } else if ((size_t)l < sizeof(*event)) {
-                        log_debug("grdrm: %s/%s: short read of %zd bytes", card->base.session->name, card->base.name, l);
-                        return 0;
                 }
 
-                for (i = 0; i < l; i += event->length) {
-                        event = (void*)&buf[i];
+                for (len = l; len > 0; len -= event->length) {
+                        event = (void*)buf;
 
-                        if (i + (ssize_t)sizeof(*event) > l || i + (ssize_t)event->length > l) {
+                        if (len < sizeof(*event) || len < event->length) {
                                 log_debug("grdrm: %s/%s: truncated event", card->base.session->name, card->base.name);
                                 break;
                         }