1/*
2 * Copyright © 2013 Keith Packard
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting documentation, and
8 * that the name of the copyright holders not be used in advertising or
9 * publicity pertaining to distribution of the software without specific,
10 * written prior permission.  The copyright holders make no representations
11 * about the suitability of this software for any purpose.  It is provided "as
12 * is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20 * OF THIS SOFTWARE.
21 */
22
23#include "present_priv.h"
24#include <misync.h>
25#include <misyncstr.h>
26#ifdef MONOTONIC_CLOCK
27#include <time.h>
28#endif
29
30/*
31 * Screen flip mode
32 *
33 * Provides the default mode for drivers, that do not
34 * support flips and the full screen flip mode.
35 *
36 */
37
38static uint64_t present_scmd_event_id;
39
40static struct xorg_list present_exec_queue;
41static struct xorg_list present_flip_queue;
42
43static void
44present_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc);
45
46static inline PixmapPtr
47present_flip_pending_pixmap(ScreenPtr screen)
48{
49    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
50
51    if (!screen_priv)
52        return NULL;
53
54    if (!screen_priv->flip_pending)
55        return NULL;
56
57    return screen_priv->flip_pending->pixmap;
58}
59
60static Bool
61present_check_flip(RRCrtcPtr            crtc,
62                   WindowPtr            window,
63                   PixmapPtr            pixmap,
64                   Bool                 sync_flip,
65                   RegionPtr            valid,
66                   int16_t              x_off,
67                   int16_t              y_off,
68                   PresentFlipReason   *reason)
69{
70    ScreenPtr                   screen = window->drawable.pScreen;
71    PixmapPtr                   window_pixmap;
72    WindowPtr                   root = screen->root;
73    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
74
75    if (crtc) {
76       screen_priv = present_screen_priv(crtc->pScreen);
77    }
78    if (reason)
79        *reason = PRESENT_FLIP_REASON_UNKNOWN;
80
81    if (!screen_priv)
82        return FALSE;
83
84    if (!screen_priv->info)
85        return FALSE;
86
87    if (!crtc)
88        return FALSE;
89
90    /* Check to see if the driver supports flips at all */
91    if (!screen_priv->info->flip)
92        return FALSE;
93
94    /* Make sure the window hasn't been redirected with Composite */
95    window_pixmap = screen->GetWindowPixmap(window);
96    if (window_pixmap != screen->GetScreenPixmap(screen) &&
97        window_pixmap != screen_priv->flip_pixmap &&
98        window_pixmap != present_flip_pending_pixmap(screen))
99        return FALSE;
100
101    /* Check for full-screen window */
102    if (!RegionEqual(&window->clipList, &root->winSize)) {
103        return FALSE;
104    }
105
106    /* Source pixmap must align with window exactly */
107    if (x_off || y_off) {
108        return FALSE;
109    }
110
111    /* Make sure the area marked as valid fills the screen */
112    if (valid && !RegionEqual(valid, &root->winSize)) {
113        return FALSE;
114    }
115
116    /* Does the window match the pixmap exactly? */
117    if (window->drawable.x != 0 || window->drawable.y != 0 ||
118#ifdef COMPOSITE
119        window->drawable.x != pixmap->screen_x || window->drawable.y != pixmap->screen_y ||
120#endif
121        window->drawable.width != pixmap->drawable.width ||
122        window->drawable.height != pixmap->drawable.height) {
123        return FALSE;
124    }
125
126    /* Ask the driver for permission */
127    if (screen_priv->info->version >= 1 && screen_priv->info->check_flip2) {
128        if (!(*screen_priv->info->check_flip2) (crtc, window, pixmap, sync_flip, reason)) {
129            DebugPresent(("\td %08" PRIx32 " -> %08" PRIx32 "\n", window->drawable.id, pixmap ? pixmap->drawable.id : 0));
130            return FALSE;
131        }
132    } else if (screen_priv->info->check_flip) {
133        if (!(*screen_priv->info->check_flip) (crtc, window, pixmap, sync_flip)) {
134            DebugPresent(("\td %08" PRIx32 " -> %08" PRIx32 "\n", window->drawable.id, pixmap ? pixmap->drawable.id : 0));
135            return FALSE;
136        }
137    }
138
139    return TRUE;
140}
141
142static Bool
143present_flip(RRCrtcPtr crtc,
144             uint64_t event_id,
145             uint64_t target_msc,
146             PixmapPtr pixmap,
147             Bool sync_flip)
148{
149    ScreenPtr                   screen = crtc->pScreen;
150    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
151
152    return (*screen_priv->info->flip) (crtc, event_id, target_msc, pixmap, sync_flip);
153}
154
155static RRCrtcPtr
156present_scmd_get_crtc(present_screen_priv_ptr screen_priv, WindowPtr window)
157{
158    if (!screen_priv->info)
159        return NULL;
160
161    if (!screen_priv->info->get_crtc)
162        return NULL;
163
164    return (*screen_priv->info->get_crtc)(window);
165}
166
167static uint32_t
168present_scmd_query_capabilities(present_screen_priv_ptr screen_priv)
169{
170    if (!screen_priv->info)
171        return 0;
172
173    return screen_priv->info->capabilities;
174}
175
176static int
177present_get_ust_msc(ScreenPtr screen, RRCrtcPtr crtc, uint64_t *ust, uint64_t *msc)
178{
179    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
180    present_screen_priv_ptr     crtc_screen_priv = screen_priv;
181    if (crtc)
182        crtc_screen_priv = present_screen_priv(crtc->pScreen);
183
184    if (crtc == NULL)
185        return present_fake_get_ust_msc(screen, ust, msc);
186    else
187        return (*crtc_screen_priv->info->get_ust_msc)(crtc, ust, msc);
188}
189
190static void
191present_flush(WindowPtr window)
192{
193    ScreenPtr                   screen = window->drawable.pScreen;
194    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
195
196    if (!screen_priv)
197        return;
198
199    if (!screen_priv->info)
200        return;
201
202    if (!screen_priv->info->flush)
203        return;
204
205    (*screen_priv->info->flush) (window);
206}
207
208static int
209present_queue_vblank(ScreenPtr screen,
210                     WindowPtr window,
211                     RRCrtcPtr crtc,
212                     uint64_t event_id,
213                     uint64_t msc)
214{
215    Bool                        ret;
216
217    if (crtc == NULL)
218        ret = present_fake_queue_vblank(screen, event_id, msc);
219    else
220    {
221        present_screen_priv_ptr     screen_priv = present_screen_priv(crtc->pScreen);
222        ret = (*screen_priv->info->queue_vblank) (crtc, event_id, msc);
223    }
224    return ret;
225}
226
227/*
228 * When the wait fence or previous flip is completed, it's time
229 * to re-try the request
230 */
231static void
232present_re_execute(present_vblank_ptr vblank)
233{
234    uint64_t            ust = 0, crtc_msc = 0;
235
236    if (vblank->crtc)
237        (void) present_get_ust_msc(vblank->screen, vblank->crtc, &ust, &crtc_msc);
238
239    present_execute(vblank, ust, crtc_msc);
240}
241
242static void
243present_flip_try_ready(ScreenPtr screen)
244{
245    present_vblank_ptr  vblank;
246
247    xorg_list_for_each_entry(vblank, &present_flip_queue, event_queue) {
248        if (vblank->queued) {
249            present_re_execute(vblank);
250            return;
251        }
252    }
253}
254
255static void
256present_flip_idle(ScreenPtr screen)
257{
258    present_screen_priv_ptr screen_priv = present_screen_priv(screen);
259
260    if (screen_priv->flip_pixmap) {
261        present_pixmap_idle(screen_priv->flip_pixmap, screen_priv->flip_window,
262                            screen_priv->flip_serial, screen_priv->flip_idle_fence);
263        if (screen_priv->flip_idle_fence)
264            present_fence_destroy(screen_priv->flip_idle_fence);
265        dixDestroyPixmap(screen_priv->flip_pixmap, screen_priv->flip_pixmap->drawable.id);
266        screen_priv->flip_crtc = NULL;
267        screen_priv->flip_window = NULL;
268        screen_priv->flip_serial = 0;
269        screen_priv->flip_pixmap = NULL;
270        screen_priv->flip_idle_fence = NULL;
271    }
272}
273
274void
275present_restore_screen_pixmap(ScreenPtr screen)
276{
277    present_screen_priv_ptr screen_priv = present_screen_priv(screen);
278    PixmapPtr screen_pixmap = (*screen->GetScreenPixmap)(screen);
279    PixmapPtr flip_pixmap;
280    WindowPtr flip_window;
281
282    if (screen_priv->flip_pending) {
283        flip_window = screen_priv->flip_pending->window;
284        flip_pixmap = screen_priv->flip_pending->pixmap;
285    } else {
286        flip_window = screen_priv->flip_window;
287        flip_pixmap = screen_priv->flip_pixmap;
288    }
289
290    assert (flip_pixmap);
291
292    /* Update the screen pixmap with the current flip pixmap contents
293     * Only do this the first time for a particular unflip operation, or
294     * we'll probably scribble over other windows
295     */
296    if (screen->root && screen->GetWindowPixmap(screen->root) == flip_pixmap)
297        present_copy_region(&screen_pixmap->drawable, flip_pixmap, NULL, 0, 0);
298
299    /* Switch back to using the screen pixmap now to avoid
300     * 2D applications drawing to the wrong pixmap.
301     */
302    if (flip_window)
303        present_set_tree_pixmap(flip_window, flip_pixmap, screen_pixmap);
304    if (screen->root)
305        present_set_tree_pixmap(screen->root, NULL, screen_pixmap);
306}
307
308void
309present_set_abort_flip(ScreenPtr screen)
310{
311    present_screen_priv_ptr screen_priv = present_screen_priv(screen);
312
313    if (!screen_priv->flip_pending->abort_flip) {
314        present_restore_screen_pixmap(screen);
315        screen_priv->flip_pending->abort_flip = TRUE;
316    }
317}
318
319static void
320present_unflip(ScreenPtr screen)
321{
322    present_screen_priv_ptr screen_priv = present_screen_priv(screen);
323
324    assert (!screen_priv->unflip_event_id);
325    assert (!screen_priv->flip_pending);
326
327    present_restore_screen_pixmap(screen);
328
329    screen_priv->unflip_event_id = ++present_scmd_event_id;
330    DebugPresent(("u %" PRIu64 "\n", screen_priv->unflip_event_id));
331    (*screen_priv->info->unflip) (screen, screen_priv->unflip_event_id);
332}
333
334static void
335present_flip_notify(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc)
336{
337    ScreenPtr                   screen = vblank->screen;
338    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
339
340    DebugPresent(("\tn %" PRIu64 " %p %" PRIu64 " %" PRIu64 ": %08" PRIx32 " -> %08" PRIx32 "\n",
341                  vblank->event_id, vblank, vblank->exec_msc, vblank->target_msc,
342                  vblank->pixmap ? vblank->pixmap->drawable.id : 0,
343                  vblank->window ? vblank->window->drawable.id : 0));
344
345    assert (vblank == screen_priv->flip_pending);
346
347    present_flip_idle(screen);
348
349    xorg_list_del(&vblank->event_queue);
350
351    /* Transfer reference for pixmap and fence from vblank to screen_priv */
352    screen_priv->flip_crtc = vblank->crtc;
353    screen_priv->flip_window = vblank->window;
354    screen_priv->flip_serial = vblank->serial;
355    screen_priv->flip_pixmap = vblank->pixmap;
356    screen_priv->flip_sync = vblank->sync_flip;
357    screen_priv->flip_idle_fence = vblank->idle_fence;
358
359    vblank->pixmap = NULL;
360    vblank->idle_fence = NULL;
361
362    screen_priv->flip_pending = NULL;
363
364    if (vblank->abort_flip)
365        present_unflip(screen);
366
367    present_vblank_notify(vblank, PresentCompleteKindPixmap, PresentCompleteModeFlip, ust, crtc_msc);
368    present_vblank_destroy(vblank);
369
370    present_flip_try_ready(screen);
371}
372
373void
374present_event_notify(uint64_t event_id, uint64_t ust, uint64_t msc)
375{
376    present_vblank_ptr  vblank;
377    int                 s;
378
379    if (!event_id)
380        return;
381    DebugPresent(("\te %" PRIu64 " ust %" PRIu64 " msc %" PRIu64 "\n", event_id, ust, msc));
382    xorg_list_for_each_entry(vblank, &present_exec_queue, event_queue) {
383        int64_t match = event_id - vblank->event_id;
384        if (match == 0) {
385            present_execute(vblank, ust, msc);
386            return;
387        }
388    }
389    xorg_list_for_each_entry(vblank, &present_flip_queue, event_queue) {
390        if (vblank->event_id == event_id) {
391            if (vblank->queued)
392                present_execute(vblank, ust, msc);
393            else
394                present_flip_notify(vblank, ust, msc);
395            return;
396        }
397    }
398
399    for (s = 0; s < screenInfo.numScreens; s++) {
400        ScreenPtr               screen = screenInfo.screens[s];
401        present_screen_priv_ptr screen_priv = present_screen_priv(screen);
402
403        if (event_id == screen_priv->unflip_event_id) {
404            DebugPresent(("\tun %" PRIu64 "\n", event_id));
405            screen_priv->unflip_event_id = 0;
406            present_flip_idle(screen);
407            present_flip_try_ready(screen);
408            return;
409        }
410    }
411}
412
413/*
414 * 'window' is being reconfigured. Check to see if it is involved
415 * in flipping and clean up as necessary
416 */
417static void
418present_check_flip_window (WindowPtr window)
419{
420    ScreenPtr                   screen = window->drawable.pScreen;
421    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
422    present_window_priv_ptr     window_priv = present_window_priv(window);
423    present_vblank_ptr          flip_pending = screen_priv->flip_pending;
424    present_vblank_ptr          vblank;
425    PresentFlipReason           reason;
426
427    /* If this window hasn't ever been used with Present, it can't be
428     * flipping
429     */
430    if (!window_priv)
431        return;
432
433    if (screen_priv->unflip_event_id)
434        return;
435
436    if (flip_pending) {
437        /*
438         * Check pending flip
439         */
440        if (flip_pending->window == window) {
441            if (!present_check_flip(flip_pending->crtc, window, flip_pending->pixmap,
442                                    flip_pending->sync_flip, NULL, 0, 0, NULL))
443                present_set_abort_flip(screen);
444        }
445    } else {
446        /*
447         * Check current flip
448         */
449        if (window == screen_priv->flip_window) {
450            if (!present_check_flip(screen_priv->flip_crtc, window, screen_priv->flip_pixmap, screen_priv->flip_sync, NULL, 0, 0, NULL))
451                present_unflip(screen);
452        }
453    }
454
455    /* Now check any queued vblanks */
456    xorg_list_for_each_entry(vblank, &window_priv->vblank, window_list) {
457        if (vblank->queued && vblank->flip && !present_check_flip(vblank->crtc, window, vblank->pixmap, vblank->sync_flip, NULL, 0, 0, &reason)) {
458            vblank->flip = FALSE;
459            vblank->reason = reason;
460            if (vblank->sync_flip)
461                vblank->exec_msc = vblank->target_msc;
462        }
463    }
464}
465
466static Bool
467present_scmd_can_window_flip(WindowPtr window)
468{
469    ScreenPtr                   screen = window->drawable.pScreen;
470    PixmapPtr                   window_pixmap;
471    WindowPtr                   root = screen->root;
472    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
473
474    if (!screen_priv)
475        return FALSE;
476
477    if (!screen_priv->info)
478        return FALSE;
479
480    /* Check to see if the driver supports flips at all */
481    if (!screen_priv->info->flip)
482        return FALSE;
483
484    /* Make sure the window hasn't been redirected with Composite */
485    window_pixmap = screen->GetWindowPixmap(window);
486    if (window_pixmap != screen->GetScreenPixmap(screen) &&
487        window_pixmap != screen_priv->flip_pixmap &&
488        window_pixmap != present_flip_pending_pixmap(screen))
489        return FALSE;
490
491    /* Check for full-screen window */
492    if (!RegionEqual(&window->clipList, &root->winSize)) {
493        return FALSE;
494    }
495
496    /* Does the window match the pixmap exactly? */
497    if (window->drawable.x != 0 || window->drawable.y != 0) {
498        return FALSE;
499    }
500
501    return TRUE;
502}
503
504/*
505 * Clean up any pending or current flips for this window
506 */
507static void
508present_scmd_clear_window_flip(WindowPtr window)
509{
510    ScreenPtr                   screen = window->drawable.pScreen;
511    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
512    present_vblank_ptr          flip_pending = screen_priv->flip_pending;
513
514    if (flip_pending && flip_pending->window == window) {
515        present_set_abort_flip(screen);
516        flip_pending->window = NULL;
517    }
518    if (screen_priv->flip_window == window) {
519        present_restore_screen_pixmap(screen);
520        screen_priv->flip_window = NULL;
521    }
522}
523
524/*
525 * Once the required MSC has been reached, execute the pending request.
526 *
527 * For requests to actually present something, either blt contents to
528 * the screen or queue a frame buffer swap.
529 *
530 * For requests to just get the current MSC/UST combo, skip that part and
531 * go straight to event delivery
532 */
533
534static void
535present_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc)
536{
537    WindowPtr                   window = vblank->window;
538    ScreenPtr                   screen = window->drawable.pScreen;
539    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
540    if (vblank && vblank->crtc) {
541        screen_priv=present_screen_priv(vblank->crtc->pScreen);
542    }
543
544    if (present_execute_wait(vblank, crtc_msc))
545        return;
546
547    if (vblank->flip && vblank->pixmap && vblank->window) {
548        if (screen_priv->flip_pending || screen_priv->unflip_event_id) {
549            DebugPresent(("\tr %" PRIu64 " %p (pending %p unflip %" PRIu64 ")\n",
550                          vblank->event_id, vblank,
551                          screen_priv->flip_pending, screen_priv->unflip_event_id));
552            xorg_list_del(&vblank->event_queue);
553            xorg_list_append(&vblank->event_queue, &present_flip_queue);
554            vblank->flip_ready = TRUE;
555            return;
556        }
557    }
558
559    xorg_list_del(&vblank->event_queue);
560    xorg_list_del(&vblank->window_list);
561    vblank->queued = FALSE;
562
563    if (vblank->pixmap && vblank->window) {
564
565        if (vblank->flip) {
566
567            DebugPresent(("\tf %" PRIu64 " %p %" PRIu64 ": %08" PRIx32 " -> %08" PRIx32 "\n",
568                          vblank->event_id, vblank, crtc_msc,
569                          vblank->pixmap->drawable.id, vblank->window->drawable.id));
570
571            /* Prepare to flip by placing it in the flip queue and
572             * and sticking it into the flip_pending field
573             */
574            screen_priv->flip_pending = vblank;
575
576            xorg_list_add(&vblank->event_queue, &present_flip_queue);
577            /* Try to flip
578             */
579            if (present_flip(vblank->crtc, vblank->event_id, vblank->target_msc, vblank->pixmap, vblank->sync_flip)) {
580                RegionPtr damage;
581
582                /* Fix window pixmaps:
583                 *  1) Restore previous flip window pixmap
584                 *  2) Set current flip window pixmap to the new pixmap
585                 */
586                if (screen_priv->flip_window && screen_priv->flip_window != window)
587                    present_set_tree_pixmap(screen_priv->flip_window,
588                                            screen_priv->flip_pixmap,
589                                            (*screen->GetScreenPixmap)(screen));
590                present_set_tree_pixmap(vblank->window, NULL, vblank->pixmap);
591                present_set_tree_pixmap(screen->root, NULL, vblank->pixmap);
592
593                /* Report update region as damaged
594                 */
595                if (vblank->update) {
596                    damage = vblank->update;
597                    RegionIntersect(damage, damage, &window->clipList);
598                } else
599                    damage = &window->clipList;
600
601                DamageDamageRegion(&vblank->window->drawable, damage);
602                return;
603            }
604
605            xorg_list_del(&vblank->event_queue);
606            /* Oops, flip failed. Clear the flip_pending field
607              */
608            screen_priv->flip_pending = NULL;
609            vblank->flip = FALSE;
610            vblank->exec_msc = vblank->target_msc;
611        }
612        DebugPresent(("\tc %p %" PRIu64 ": %08" PRIx32 " -> %08" PRIx32 "\n",
613                      vblank, crtc_msc, vblank->pixmap->drawable.id, vblank->window->drawable.id));
614        if (screen_priv->flip_pending) {
615
616            /* Check pending flip
617             */
618            if (window == screen_priv->flip_pending->window)
619                present_set_abort_flip(screen);
620        } else if (!screen_priv->unflip_event_id) {
621
622            /* Check current flip
623             */
624            if (window == screen_priv->flip_window)
625                present_unflip(screen);
626        }
627
628        present_execute_copy(vblank, crtc_msc);
629
630        if (vblank->queued) {
631            xorg_list_add(&vblank->event_queue, &present_exec_queue);
632            xorg_list_append(&vblank->window_list,
633                             &present_get_window_priv(window, TRUE)->vblank);
634            return;
635        }
636    }
637
638    present_execute_post(vblank, ust, crtc_msc);
639}
640
641static void
642present_scmd_update_window_crtc(WindowPtr window, RRCrtcPtr crtc, uint64_t new_msc)
643{
644    present_window_priv_ptr window_priv = present_get_window_priv(window, TRUE);
645    uint64_t                old_ust, old_msc;
646
647    /* Crtc unchanged, no offset. */
648    if (crtc == window_priv->crtc)
649        return;
650
651    /* No crtc earlier to offset against, just set the crtc. */
652    if (window_priv->crtc == PresentCrtcNeverSet) {
653        window_priv->crtc = crtc;
654        return;
655    }
656
657    /* Crtc may have been turned off or be destroyed, just use whatever previous MSC we'd seen from this CRTC. */
658    if (!RRCrtcExists(window->drawable.pScreen, window_priv->crtc) ||
659        present_get_ust_msc(window->drawable.pScreen, window_priv->crtc, &old_ust, &old_msc) != Success)
660        old_msc = window_priv->msc;
661
662    window_priv->msc_offset += new_msc - old_msc;
663    window_priv->crtc = crtc;
664}
665
666static int
667present_scmd_pixmap(WindowPtr window,
668                    PixmapPtr pixmap,
669                    CARD32 serial,
670                    RegionPtr valid,
671                    RegionPtr update,
672                    int16_t x_off,
673                    int16_t y_off,
674                    RRCrtcPtr target_crtc,
675                    SyncFence *wait_fence,
676                    SyncFence *idle_fence,
677                    uint32_t options,
678                    uint64_t target_window_msc,
679                    uint64_t divisor,
680                    uint64_t remainder,
681                    present_notify_ptr notifies,
682                    int num_notifies)
683{
684    uint64_t                    ust = 0;
685    uint64_t                    target_msc;
686    uint64_t                    crtc_msc = 0;
687    int                         ret;
688    present_vblank_ptr          vblank, tmp;
689    ScreenPtr                   screen = window->drawable.pScreen;
690    present_window_priv_ptr     window_priv = present_get_window_priv(window, TRUE);
691    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
692
693    if (!window_priv)
694        return BadAlloc;
695
696    if (!screen_priv || !screen_priv->info)
697        target_crtc = NULL;
698    else if (!target_crtc) {
699        /* Update the CRTC if we have a pixmap or we don't have a CRTC
700         */
701        if (!pixmap)
702            target_crtc = window_priv->crtc;
703
704        if (!target_crtc || target_crtc == PresentCrtcNeverSet)
705            target_crtc = present_get_crtc(window);
706    }
707
708    ret = present_get_ust_msc(screen, target_crtc, &ust, &crtc_msc);
709
710    present_scmd_update_window_crtc(window, target_crtc, crtc_msc);
711
712    if (ret == Success) {
713        /* Stash the current MSC away in case we need it later
714         */
715        window_priv->msc = crtc_msc;
716    }
717
718    target_msc = present_get_target_msc(target_window_msc + window_priv->msc_offset,
719                                        crtc_msc,
720                                        divisor,
721                                        remainder,
722                                        options);
723
724    /*
725     * Look for a matching presentation already on the list and
726     * don't bother doing the previous one if this one will overwrite it
727     * in the same frame
728     */
729
730    if (!update && pixmap) {
731        xorg_list_for_each_entry_safe(vblank, tmp, &window_priv->vblank, window_list) {
732
733            if (!vblank->pixmap)
734                continue;
735
736            if (!vblank->queued)
737                continue;
738
739            if (vblank->crtc != target_crtc || vblank->target_msc != target_msc)
740                continue;
741
742            present_vblank_scrap(vblank);
743            if (vblank->flip_ready)
744                present_re_execute(vblank);
745        }
746    }
747
748    vblank = present_vblank_create(window,
749                                   pixmap,
750                                   serial,
751                                   valid,
752                                   update,
753                                   x_off,
754                                   y_off,
755                                   target_crtc,
756                                   wait_fence,
757                                   idle_fence,
758                                   options,
759                                   screen_priv->info ? screen_priv->info->capabilities : 0,
760                                   notifies,
761                                   num_notifies,
762                                   target_msc,
763                                   crtc_msc);
764
765    if (!vblank)
766        return BadAlloc;
767
768    vblank->event_id = ++present_scmd_event_id;
769
770    if (vblank->flip && vblank->sync_flip)
771        vblank->exec_msc--;
772
773    xorg_list_append(&vblank->event_queue, &present_exec_queue);
774    vblank->queued = TRUE;
775    if (msc_is_after(vblank->exec_msc, crtc_msc)) {
776        ret = present_queue_vblank(screen, window, target_crtc, vblank->event_id, vblank->exec_msc);
777        if (ret == Success)
778            return Success;
779
780        DebugPresent(("present_queue_vblank failed\n"));
781    }
782
783    present_execute(vblank, ust, crtc_msc);
784
785    return Success;
786}
787
788static void
789present_scmd_abort_vblank(ScreenPtr screen, WindowPtr window, RRCrtcPtr crtc, uint64_t event_id, uint64_t msc)
790{
791    present_vblank_ptr  vblank;
792
793    if (crtc == NULL)
794        present_fake_abort_vblank(screen, event_id, msc);
795    else
796    {
797        present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
798
799        (*screen_priv->info->abort_vblank) (crtc, event_id, msc);
800    }
801
802    xorg_list_for_each_entry(vblank, &present_exec_queue, event_queue) {
803        int64_t match = event_id - vblank->event_id;
804        if (match == 0) {
805            xorg_list_del(&vblank->event_queue);
806            vblank->queued = FALSE;
807            return;
808        }
809    }
810    xorg_list_for_each_entry(vblank, &present_flip_queue, event_queue) {
811        if (vblank->event_id == event_id) {
812            xorg_list_del(&vblank->event_queue);
813            vblank->queued = FALSE;
814            return;
815        }
816    }
817}
818
819static void
820present_scmd_flip_destroy(ScreenPtr screen)
821{
822    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
823
824    /* Reset window pixmaps back to the screen pixmap */
825    if (screen_priv->flip_pending)
826        present_set_abort_flip(screen);
827
828    /* Drop reference to any pending flip or unflip pixmaps. */
829    present_flip_idle(screen);
830}
831
832void
833present_scmd_init_mode_hooks(present_screen_priv_ptr screen_priv)
834{
835    screen_priv->query_capabilities =   &present_scmd_query_capabilities;
836    screen_priv->get_crtc           =   &present_scmd_get_crtc;
837
838    screen_priv->check_flip         =   &present_check_flip;
839    screen_priv->check_flip_window  =   &present_check_flip_window;
840    screen_priv->can_window_flip    =   &present_scmd_can_window_flip;
841    screen_priv->clear_window_flip  =   &present_scmd_clear_window_flip;
842
843    screen_priv->present_pixmap     =   &present_scmd_pixmap;
844
845    screen_priv->queue_vblank       =   &present_queue_vblank;
846    screen_priv->flush              =   &present_flush;
847    screen_priv->re_execute         =   &present_re_execute;
848
849    screen_priv->abort_vblank       =   &present_scmd_abort_vblank;
850    screen_priv->flip_destroy       =   &present_scmd_flip_destroy;
851}
852
853Bool
854present_init(void)
855{
856    xorg_list_init(&present_exec_queue);
857    xorg_list_init(&present_flip_queue);
858    present_fake_queue_init();
859    return TRUE;
860}
861