X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Flibsystemd-terminal%2Fgrdev.c;h=3b3cf2737bbf59a6538c218cd2627722912037be;hb=efd51554aea0f226a7f6704a72aa6a3c9fc6cb2f;hp=397da1b205a1a74cb6f4ed5a67ba47403ddf181e;hpb=3ec19e5d91d3d705682fee62a509801737c56c1e;p=elogind.git diff --git a/src/libsystemd-terminal/grdev.c b/src/libsystemd-terminal/grdev.c index 397da1b20..3b3cf2737 100644 --- a/src/libsystemd-terminal/grdev.c +++ b/src/libsystemd-terminal/grdev.c @@ -54,10 +54,10 @@ static inline grdev_tile *tile_leftmost(grdev_tile *tile) { } #define TILE_FOREACH(_root, _i) \ - for (_i = tile_leftmost(_root); _i; _i = tile_leftmost(_i->childs_by_node_next) ? : _i->parent) + for (_i = tile_leftmost(_root); _i; _i = tile_leftmost(_i->children_by_node_next) ? : _i->parent) #define TILE_FOREACH_SAFE(_root, _i, _next) \ - for (_i = tile_leftmost(_root); _i && ((_next = tile_leftmost(_i->childs_by_node_next) ? : _i->parent), true); _i = _next) + for (_i = tile_leftmost(_root); _i && ((_next = tile_leftmost(_i->children_by_node_next) ? : _i->parent), true); _i = _next) static void tile_link(grdev_tile *tile, grdev_tile *parent) { grdev_display *display; @@ -73,8 +73,8 @@ static void tile_link(grdev_tile *tile, grdev_tile *parent) { assert(!display || !display->enabled); - ++parent->node.n_childs; - LIST_PREPEND(childs_by_node, parent->node.child_list, tile); + ++parent->node.n_children; + LIST_PREPEND(children_by_node, parent->node.child_list, tile); tile->parent = parent; if (display) { @@ -105,10 +105,10 @@ static void tile_unlink(grdev_tile *tile) { assert(parent->type == GRDEV_TILE_NODE); assert(parent->display == display); - assert(parent->node.n_childs > 0); + assert(parent->node.n_children > 0); - --parent->node.n_childs; - LIST_REMOVE(childs_by_node, parent->node.child_list, tile); + --parent->node.n_children; + LIST_REMOVE(children_by_node, parent->node.child_list, tile); tile->parent = NULL; if (display) { @@ -127,14 +127,14 @@ static void tile_unlink(grdev_tile *tile) { * we must take care to not leave them around. Therefore, whenever we * unlink any part of a tree, we also destroy the parent, in case it's * now stale. - * Parents are stale if they have no childs and either have no display + * Parents are stale if they have no children and either have no display * or if they are intermediate nodes (i.e, they have a parent). * This means, you can easily create trees, but you can never partially * move or destruct them so far. They're always reduced to minimal form * if you cut them. This might change later, but so far we didn't need * partial destruction or the ability to move whole trees. */ - if (parent->node.n_childs < 1 && (parent->parent || !parent->display)) + if (parent->node.n_children < 1 && (parent->parent || !parent->display)) grdev_tile_free(parent); } @@ -158,6 +158,7 @@ int grdev_tile_new_leaf(grdev_tile **out, grdev_pipe *pipe) { _cleanup_(grdev_tile_freep) grdev_tile *tile = NULL; int r; + assert_return(out, -EINVAL); assert_return(pipe, -EINVAL); assert_return(!pipe->tile, -EINVAL); @@ -207,7 +208,7 @@ grdev_tile *grdev_tile_free(grdev_tile *tile) { case GRDEV_TILE_NODE: assert(!tile->parent); assert(!tile->display); - assert(tile->node.n_childs == 0); + assert(tile->node.n_children == 0); break; } @@ -282,6 +283,36 @@ grdev_display *grdev_display_free(grdev_display *display) { return NULL; } +void grdev_display_set_userdata(grdev_display *display, void *userdata) { + assert(display); + + display->userdata = userdata; +} + +void *grdev_display_get_userdata(grdev_display *display) { + assert_return(display, NULL); + + return display->userdata; +} + +const char *grdev_display_get_name(grdev_display *display) { + assert_return(display, NULL); + + return display->name; +} + +uint32_t grdev_display_get_width(grdev_display *display) { + assert_return(display, 0); + + return display->width; +} + +uint32_t grdev_display_get_height(grdev_display *display) { + assert_return(display, 0); + + return display->height; +} + bool grdev_display_is_enabled(grdev_display *display) { return display && display->enabled; } @@ -312,7 +343,7 @@ void grdev_display_disable(grdev_display *display) { } } -const grdev_display_target *grdev_display_next_target(grdev_display *display, const grdev_display_target *prev, uint64_t minage) { +const grdev_display_target *grdev_display_next_target(grdev_display *display, const grdev_display_target *prev) { grdev_display_cache *cache; size_t idx; @@ -327,7 +358,7 @@ const grdev_display_target *grdev_display_next_target(grdev_display *display, co assert(cache->pipe->tile->display == display); assert(display->pipes >= cache); - idx = (cache - display->pipes) / sizeof(*cache) + 1; + idx = cache - display->pipes + 1; } else { idx = 0; } @@ -343,52 +374,38 @@ const grdev_display_target *grdev_display_next_target(grdev_display *display, co if (!pipe->running || !pipe->enabled) continue; - /* if front-buffer is up-to-date, there's nothing to do */ - if (minage > 0 && pipe->front && pipe->front->age >= minage) - continue; - /* find suitable back-buffer */ - if (!(fb = pipe->back)) { - if (!pipe->vtable->target || !(fb = pipe->vtable->target(pipe))) + if (!pipe->back) { + if (!pipe->vtable->target) + continue; + if (!(fb = pipe->vtable->target(pipe))) continue; - } - /* if back-buffer is up-to-date, schedule flip */ - if (minage > 0 && fb->age >= minage) { - grdev_display_flip_target(display, target, fb->age); - continue; + assert(fb == pipe->back); } - /* we have an out-of-date back-buffer; return for redraw */ - target->fb = fb; + target->front = pipe->front; + target->back = pipe->back; + return target; } return NULL; } -void grdev_display_flip_target(grdev_display *display, const grdev_display_target *target, uint64_t age) { +void grdev_display_flip_target(grdev_display *display, const grdev_display_target *target) { grdev_display_cache *cache; - size_t i; assert(display); assert(!display->modified); assert(display->enabled); assert(target); - assert(target->fb); cache = container_of(target, grdev_display_cache, target); assert(cache->pipe); assert(cache->pipe->tile->display == display); - /* reset age of all FB on overflow */ - if (age < target->fb->age) - for (i = 0; i < cache->pipe->max_fbs; ++i) - if (cache->pipe->fbs[i]) - cache->pipe->fbs[i]->age = 0; - - ((grdev_fb*)target->fb)->age = age; cache->pipe->flip = true; } @@ -454,10 +471,10 @@ static void display_cache_targets(grdev_display *display) { assert(display); - /* depth-first with childs before parent */ + /* depth-first with children before parent */ for (tile = tile_leftmost(display->tile); tile; - tile = tile_leftmost(tile->childs_by_node_next) ? : tile->parent) { + tile = tile_leftmost(tile->children_by_node_next) ? : tile->parent) { if (tile->type == GRDEV_TILE_LEAF) { grdev_pipe *p; @@ -486,7 +503,7 @@ static void display_cache_targets(grdev_display *display) { } else { grdev_tile *child, *l; - /* We're now at a node with all it's childs already + /* We're now at a node with all its children already * computed (depth-first, child before parent). We * first need to know the size of our tile, then we * recurse into all leafs and update their cache. */ @@ -494,7 +511,7 @@ static void display_cache_targets(grdev_display *display) { tile->cache_w = 0; tile->cache_h = 0; - LIST_FOREACH(childs_by_node, child, tile->node.child_list) { + LIST_FOREACH(children_by_node, child, tile->node.child_list) { if (child->x + child->cache_w > tile->cache_w) tile->cache_w = child->x + child->cache_w; if (child->y + child->cache_h > tile->cache_h) @@ -553,13 +570,15 @@ static bool display_cache(grdev_display *display) { } display_cache_targets(display); + display->width = display->tile->cache_w; + display->height = display->tile->cache_h; r = 0; out: if (r < 0) - log_debug("grdev: %s/%s: cannot cache pipes: %s", - display->session->name, display->name, strerror(-r)); + log_debug_errno(r, "grdev: %s/%s: cannot cache pipes: %m", + display->session->name, display->name); return true; } @@ -574,6 +593,13 @@ grdev_pipe *grdev_find_pipe(grdev_card *card, const char *name) { return hashmap_get(card->pipe_map, name); } +static int pipe_vsync_fn(sd_event_source *src, uint64_t usec, void *userdata) { + grdev_pipe *pipe = userdata; + + grdev_pipe_frame(pipe); + return 0; +} + int grdev_pipe_add(grdev_pipe *pipe, const char *name, size_t n_fbs) { int r; @@ -585,6 +611,7 @@ int grdev_pipe_add(grdev_pipe *pipe, const char *name, size_t n_fbs) { assert_return(!pipe->cache, -EINVAL); assert_return(pipe->width > 0, -EINVAL); assert_return(pipe->height > 0, -EINVAL); + assert_return(pipe->vrefresh > 0, -EINVAL); assert_return(!pipe->enabled, -EINVAL); assert_return(!pipe->running, -EINVAL); assert_return(name, -EINVAL); @@ -605,6 +632,20 @@ int grdev_pipe_add(grdev_pipe *pipe, const char *name, size_t n_fbs) { if (r < 0) return r; + r = sd_event_add_time(pipe->card->session->context->event, + &pipe->vsync_src, + CLOCK_MONOTONIC, + 0, + 10 * USEC_PER_MSEC, + pipe_vsync_fn, + pipe); + if (r < 0) + return r; + + r = sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_OFF); + if (r < 0) + return r; + r = hashmap_put(pipe->card->pipe_map, pipe->name, pipe); if (r < 0) return r; @@ -633,6 +674,7 @@ grdev_pipe *grdev_pipe_free(grdev_pipe *pipe) { tmp = *pipe; pipe->vtable->free(pipe); + sd_event_source_unref(tmp.vsync_src); grdev_tile_free(tmp.tile); card_modified(tmp.card); free(tmp.fbs); @@ -666,7 +708,7 @@ void grdev_pipe_ready(grdev_pipe *pipe, bool running) { /* grdev_pipe_ready() is used by backends to notify about pipe state * changed. If a pipe is ready, it can be fully used by us (available, - * enabled and accessable). Backends can disable pipes at any time + * enabled and accessible). Backends can disable pipes at any time * (like for async revocation), but can only enable them from parent * context. Otherwise, we might call user-callbacks recursively. */ @@ -676,17 +718,15 @@ void grdev_pipe_ready(grdev_pipe *pipe, bool running) { pipe->running = running; /* runtime events for unused pipes are not interesting */ - if (pipe->cache) { + if (pipe->cache && pipe->enabled) { grdev_display *display = pipe->tile->display; assert(display); - if (running) { - if (pipe->enabled) - session_frame(display->session, display); - } else { + if (running) + session_frame(display->session, display); + else pipe->cache->incomplete = true; - } } } @@ -696,14 +736,44 @@ void grdev_pipe_frame(grdev_pipe *pipe) { assert(pipe); /* if pipe is unused, ignore any frame events */ - if (!pipe->cache) + if (!pipe->cache || !pipe->enabled) return; display = pipe->tile->display; assert(display); - if (pipe->enabled) - session_frame(display->session, display); + grdev_pipe_schedule(pipe, 0); + session_frame(display->session, display); +} + +void grdev_pipe_schedule(grdev_pipe *pipe, uint64_t frames) { + int r; + uint64_t ts; + + if (!frames) { + sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_OFF); + return; + } + + r = sd_event_now(pipe->card->session->context->event, CLOCK_MONOTONIC, &ts); + if (r < 0) + goto error; + + ts += frames * USEC_PER_MSEC * 1000ULL / pipe->vrefresh; + + r = sd_event_source_set_time(pipe->vsync_src, ts); + if (r < 0) + goto error; + + r = sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_ONESHOT); + if (r < 0) + goto error; + + return; + +error: + log_debug_errno(r, "grdev: %s/%s/%s: cannot schedule vsync timer: %m", + pipe->card->session->name, pipe->card->name, pipe->name); } /* @@ -1106,8 +1176,8 @@ void grdev_session_add_drm(grdev_session *session, struct udev_device *ud) { r = grdev_drm_card_new(&card, session, ud); if (r < 0) { - log_debug("grdev: %s: cannot add DRM device for %s: %s", - session->name, udev_device_get_syspath(ud), strerror(-r)); + log_debug_errno(r, "grdev: %s: cannot add DRM device for %s: %m", + session->name, udev_device_get_syspath(ud)); return; } @@ -1199,8 +1269,8 @@ static void session_configure(grdev_session *session) { } else if (!display) { r = grdev_display_new(&display, session, pipe->name); if (r < 0) { - log_debug("grdev: %s/%s: cannot create display for pipe %s: %s", - session->name, card->name, pipe->name, strerror(-r)); + log_debug_errno(r, "grdev: %s/%s: cannot create display for pipe %s: %m", + session->name, card->name, pipe->name); continue; } }