diff options
Diffstat (limited to 'xserver/present/present.c')
-rw-r--r-- | xserver/present/present.c | 154 |
1 files changed, 114 insertions, 40 deletions
diff --git a/xserver/present/present.c b/xserver/present/present.c index beb4ff03a..105e2bf47 100644 --- a/xserver/present/present.c +++ b/xserver/present/present.c @@ -46,6 +46,28 @@ static void present_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc); /* + * Returns: + * TRUE if the first MSC value is after the second one + * FALSE if the first MSC value is equal to or before the second one + */ +static Bool +msc_is_after(uint64_t test, uint64_t reference) +{ + return (int64_t)(test - reference) > 0; +} + +/* + * Returns: + * TRUE if the first MSC value is equal to or after the second one + * FALSE if the first MSC value is before the second one + */ +static Bool +msc_is_equal_or_after(uint64_t test, uint64_t reference) +{ + return (int64_t)(test - reference) >= 0; +} + +/* * Copies the update region from a pixmap to the target drawable */ static void @@ -122,6 +144,10 @@ present_check_flip(RRCrtcPtr crtc, if (!screen_priv->info->flip) return FALSE; + /* Fail to flip if we have slave outputs */ + if (!xorg_list_is_empty(&screen->output_slave_list)) + return FALSE; + /* Make sure the window hasn't been redirected with Composite */ window_pixmap = screen->GetWindowPixmap(window); if (window_pixmap != screen->GetScreenPixmap(screen) && @@ -374,12 +400,17 @@ present_set_tree_pixmap_visit(WindowPtr window, void *data) } static void -present_set_tree_pixmap(WindowPtr window, PixmapPtr pixmap) +present_set_tree_pixmap(WindowPtr window, + PixmapPtr expected, + PixmapPtr pixmap) { struct pixmap_visit visit; ScreenPtr screen = window->drawable.pScreen; visit.old = (*screen->GetWindowPixmap)(window); + if (expected && visit.old != expected) + return; + visit.new = pixmap; if (visit.old == visit.new) return; @@ -387,20 +418,44 @@ present_set_tree_pixmap(WindowPtr window, PixmapPtr pixmap) } static void -present_set_abort_flip(ScreenPtr screen) +present_restore_screen_pixmap(ScreenPtr screen) { present_screen_priv_ptr screen_priv = present_screen_priv(screen); + PixmapPtr screen_pixmap = (*screen->GetScreenPixmap)(screen); + PixmapPtr flip_pixmap; + WindowPtr flip_window; + + if (screen_priv->flip_pending) { + flip_window = screen_priv->flip_pending->window; + flip_pixmap = screen_priv->flip_pending->pixmap; + } else { + flip_window = screen_priv->flip_window; + flip_pixmap = screen_priv->flip_pixmap; + } + + assert (flip_pixmap); + + /* Update the screen pixmap with the current flip pixmap contents + * Only do this the first time for a particular unflip operation, or + * we'll probably scribble over other windows + */ + if (screen->GetWindowPixmap(screen->root) == flip_pixmap) + present_copy_region(&screen_pixmap->drawable, flip_pixmap, NULL, 0, 0); /* Switch back to using the screen pixmap now to avoid * 2D applications drawing to the wrong pixmap. */ + if (flip_window) + present_set_tree_pixmap(flip_window, flip_pixmap, screen_pixmap); + present_set_tree_pixmap(screen->root, NULL, screen_pixmap); +} - if (screen_priv->flip_window) - present_set_tree_pixmap(screen_priv->flip_window, - (*screen->GetScreenPixmap)(screen)); +static void +present_set_abort_flip(ScreenPtr screen) +{ + present_screen_priv_ptr screen_priv = present_screen_priv(screen); - if (screen->root) - present_set_tree_pixmap(screen->root, (*screen->GetScreenPixmap)(screen)); + present_restore_screen_pixmap(screen); screen_priv->flip_pending->abort_flip = TRUE; } @@ -409,23 +464,12 @@ static void present_unflip(ScreenPtr screen) { present_screen_priv_ptr screen_priv = present_screen_priv(screen); - PixmapPtr pixmap = (*screen->GetScreenPixmap)(screen); assert (!screen_priv->unflip_event_id); assert (!screen_priv->flip_pending); - if (screen_priv->flip_window) - present_set_tree_pixmap(screen_priv->flip_window, pixmap); - - present_set_tree_pixmap(screen->root, pixmap); + present_restore_screen_pixmap(screen); - /* Update the screen pixmap with the current flip pixmap contents - */ - if (screen_priv->flip_pixmap && screen_priv->flip_window) { - present_copy_region(&pixmap->drawable, - screen_priv->flip_pixmap, - NULL, 0, 0); - } screen_priv->unflip_event_id = ++present_event_id; DebugPresent(("u %lld\n", screen_priv->unflip_event_id)); (*screen_priv->info->unflip) (screen, screen_priv->unflip_event_id); @@ -453,6 +497,7 @@ present_flip_notify(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc) screen_priv->flip_window = vblank->window; screen_priv->flip_serial = vblank->serial; screen_priv->flip_pixmap = vblank->pixmap; + screen_priv->flip_sync = vblank->sync_flip; screen_priv->flip_idle_fence = vblank->idle_fence; vblank->pixmap = NULL; @@ -541,15 +586,18 @@ present_check_flip_window (WindowPtr window) * Check current flip */ if (window == screen_priv->flip_window) { - if (!present_check_flip(screen_priv->flip_crtc, window, screen_priv->flip_pixmap, FALSE, NULL, 0, 0)) + if (!present_check_flip(screen_priv->flip_crtc, window, screen_priv->flip_pixmap, screen_priv->flip_sync, NULL, 0, 0)) present_unflip(screen); } } /* Now check any queued vblanks */ xorg_list_for_each_entry(vblank, &window_priv->vblank, window_list) { - if (vblank->queued && vblank->flip && !present_check_flip(vblank->crtc, window, vblank->pixmap, FALSE, NULL, 0, 0)) + if (vblank->queued && vblank->flip && !present_check_flip(vblank->crtc, window, vblank->pixmap, vblank->sync_flip, NULL, 0, 0)) { vblank->flip = FALSE; + if (vblank->sync_flip) + vblank->requeue = TRUE; + } } } @@ -583,6 +631,16 @@ present_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc) present_screen_priv_ptr screen_priv = present_screen_priv(screen); uint8_t mode; + if (vblank->requeue) { + vblank->requeue = FALSE; + if (msc_is_after(vblank->target_msc, crtc_msc) && + Success == present_queue_vblank(screen, + vblank->crtc, + vblank->event_id, + vblank->target_msc)) + return; + } + if (vblank->wait_fence) { if (!present_fence_check_triggered(vblank->wait_fence)) { present_fence_set_callback(vblank->wait_fence, present_wait_fence_triggered, vblank); @@ -629,9 +687,10 @@ present_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc) */ if (screen_priv->flip_window && screen_priv->flip_window != window) present_set_tree_pixmap(screen_priv->flip_window, - (*screen->GetScreenPixmap)(screen)); - present_set_tree_pixmap(vblank->window, vblank->pixmap); - present_set_tree_pixmap(screen->root, vblank->pixmap); + screen_priv->flip_pixmap, + (*screen->GetScreenPixmap)(screen)); + present_set_tree_pixmap(vblank->window, NULL, vblank->pixmap); + present_set_tree_pixmap(screen->root, NULL, vblank->pixmap); /* Report update region as damaged */ @@ -665,6 +724,20 @@ present_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc) if (window == screen_priv->flip_window) present_unflip(screen); } + + /* If present_flip failed, we may have to requeue for the target MSC */ + if (vblank->target_msc == crtc_msc + 1 && + Success == present_queue_vblank(screen, + vblank->crtc, + vblank->event_id, + vblank->target_msc)) { + xorg_list_add(&vblank->event_queue, &present_exec_queue); + xorg_list_append(&vblank->window_list, + &present_get_window_priv(window, TRUE)->vblank); + vblank->queued = TRUE; + return; + } + present_copy_region(&window->drawable, vblank->pixmap, vblank->update, vblank->x_off, vblank->y_off); /* present_copy_region sticks the region into a scratch GC, @@ -746,14 +819,14 @@ present_pixmap(WindowPtr window, /* Adjust target_msc to match modulus */ - if (crtc_msc >= target_msc) { + if (msc_is_equal_or_after(crtc_msc, target_msc)) { if (divisor != 0) { target_msc = crtc_msc - (crtc_msc % divisor) + remainder; if (options & PresentOptionAsync) { - if (target_msc < crtc_msc) + if (msc_is_after(crtc_msc, target_msc)) target_msc += divisor; } else { - if (target_msc <= crtc_msc) + if (msc_is_equal_or_after(crtc_msc, target_msc)) target_msc += divisor; } } else { @@ -836,19 +909,20 @@ present_pixmap(WindowPtr window, vblank->notifies = notifies; vblank->num_notifies = num_notifies; - if (!(options & PresentOptionAsync)) - vblank->sync_flip = TRUE; - - if (!(options & PresentOptionCopy) && - !((options & PresentOptionAsync) && - (!screen_priv->info || - !(screen_priv->info->capabilities & PresentCapabilityAsync))) && - pixmap != NULL && - present_check_flip (target_crtc, window, pixmap, vblank->sync_flip, valid, x_off, y_off)) - { - vblank->flip = TRUE; - if (vblank->sync_flip) + if (pixmap != NULL && + !(options & PresentOptionCopy) && + screen_priv->info) { + if (msc_is_after(target_msc, crtc_msc) && + present_check_flip (target_crtc, window, pixmap, TRUE, valid, x_off, y_off)) + { + vblank->flip = TRUE; + vblank->sync_flip = TRUE; target_msc--; + } else if ((screen_priv->info->capabilities & PresentCapabilityAsync) && + present_check_flip (target_crtc, window, pixmap, FALSE, valid, x_off, y_off)) + { + vblank->flip = TRUE; + } } if (wait_fence) { @@ -871,7 +945,7 @@ present_pixmap(WindowPtr window, xorg_list_add(&vblank->event_queue, &present_exec_queue); vblank->queued = TRUE; - if ((pixmap && target_msc >= crtc_msc) || (!pixmap && target_msc > crtc_msc)) { + if (msc_is_after(target_msc, crtc_msc)) { ret = present_queue_vblank(screen, target_crtc, vblank->event_id, target_msc); if (ret == Success) return Success; |