present_scmd.c revision ed6184df
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    return (*screen_priv->info->get_crtc)(window);
162}
163
164static uint32_t
165present_scmd_query_capabilities(present_screen_priv_ptr screen_priv)
166{
167    if (!screen_priv->info)
168        return 0;
169
170    return screen_priv->info->capabilities;
171}
172
173static int
174present_get_ust_msc(ScreenPtr screen, RRCrtcPtr crtc, uint64_t *ust, uint64_t *msc)
175{
176    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
177    present_screen_priv_ptr     crtc_screen_priv = screen_priv;
178    if (crtc)
179        crtc_screen_priv = present_screen_priv(crtc->pScreen);
180
181    if (crtc == NULL)
182        return present_fake_get_ust_msc(screen, ust, msc);
183    else
184        return (*crtc_screen_priv->info->get_ust_msc)(crtc, ust, msc);
185}
186
187static void
188present_flush(WindowPtr window)
189{
190    ScreenPtr                   screen = window->drawable.pScreen;
191    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
192
193    if (!screen_priv)
194        return;
195
196    if (!screen_priv->info)
197        return;
198
199    (*screen_priv->info->flush) (window);
200}
201
202static int
203present_queue_vblank(ScreenPtr screen,
204                     WindowPtr window,
205                     RRCrtcPtr crtc,
206                     uint64_t event_id,
207                     uint64_t msc)
208{
209    Bool                        ret;
210
211    if (crtc == NULL)
212        ret = present_fake_queue_vblank(screen, event_id, msc);
213    else
214    {
215        present_screen_priv_ptr     screen_priv = present_screen_priv(crtc->pScreen);
216        ret = (*screen_priv->info->queue_vblank) (crtc, event_id, msc);
217    }
218    return ret;
219}
220
221/*
222 * When the wait fence or previous flip is completed, it's time
223 * to re-try the request
224 */
225static void
226present_re_execute(present_vblank_ptr vblank)
227{
228    uint64_t            ust = 0, crtc_msc = 0;
229
230    if (vblank->crtc)
231        (void) present_get_ust_msc(vblank->screen, vblank->crtc, &ust, &crtc_msc);
232
233    present_execute(vblank, ust, crtc_msc);
234}
235
236static void
237present_flip_try_ready(ScreenPtr screen)
238{
239    present_vblank_ptr  vblank;
240
241    xorg_list_for_each_entry(vblank, &present_flip_queue, event_queue) {
242        if (vblank->queued) {
243            present_re_execute(vblank);
244            return;
245        }
246    }
247}
248
249static void
250present_flip_idle(ScreenPtr screen)
251{
252    present_screen_priv_ptr screen_priv = present_screen_priv(screen);
253
254    if (screen_priv->flip_pixmap) {
255        present_pixmap_idle(screen_priv->flip_pixmap, screen_priv->flip_window,
256                            screen_priv->flip_serial, screen_priv->flip_idle_fence);
257        if (screen_priv->flip_idle_fence)
258            present_fence_destroy(screen_priv->flip_idle_fence);
259        dixDestroyPixmap(screen_priv->flip_pixmap, screen_priv->flip_pixmap->drawable.id);
260        screen_priv->flip_crtc = NULL;
261        screen_priv->flip_window = NULL;
262        screen_priv->flip_serial = 0;
263        screen_priv->flip_pixmap = NULL;
264        screen_priv->flip_idle_fence = NULL;
265    }
266}
267
268void
269present_restore_screen_pixmap(ScreenPtr screen)
270{
271    present_screen_priv_ptr screen_priv = present_screen_priv(screen);
272    PixmapPtr screen_pixmap = (*screen->GetScreenPixmap)(screen);
273    PixmapPtr flip_pixmap;
274    WindowPtr flip_window;
275
276    if (screen_priv->flip_pending) {
277        flip_window = screen_priv->flip_pending->window;
278        flip_pixmap = screen_priv->flip_pending->pixmap;
279    } else {
280        flip_window = screen_priv->flip_window;
281        flip_pixmap = screen_priv->flip_pixmap;
282    }
283
284    assert (flip_pixmap);
285
286    /* Update the screen pixmap with the current flip pixmap contents
287     * Only do this the first time for a particular unflip operation, or
288     * we'll probably scribble over other windows
289     */
290    if (screen->root && screen->GetWindowPixmap(screen->root) == flip_pixmap)
291        present_copy_region(&screen_pixmap->drawable, flip_pixmap, NULL, 0, 0);
292
293    /* Switch back to using the screen pixmap now to avoid
294     * 2D applications drawing to the wrong pixmap.
295     */
296    if (flip_window)
297        present_set_tree_pixmap(flip_window, flip_pixmap, screen_pixmap);
298    if (screen->root)
299        present_set_tree_pixmap(screen->root, NULL, screen_pixmap);
300}
301
302void
303present_set_abort_flip(ScreenPtr screen)
304{
305    present_screen_priv_ptr screen_priv = present_screen_priv(screen);
306
307    if (!screen_priv->flip_pending->abort_flip) {
308        present_restore_screen_pixmap(screen);
309        screen_priv->flip_pending->abort_flip = TRUE;
310    }
311}
312
313static void
314present_unflip(ScreenPtr screen)
315{
316    present_screen_priv_ptr screen_priv = present_screen_priv(screen);
317
318    assert (!screen_priv->unflip_event_id);
319    assert (!screen_priv->flip_pending);
320
321    present_restore_screen_pixmap(screen);
322
323    screen_priv->unflip_event_id = ++present_scmd_event_id;
324    DebugPresent(("u %" PRIu64 "\n", screen_priv->unflip_event_id));
325    (*screen_priv->info->unflip) (screen, screen_priv->unflip_event_id);
326}
327
328static void
329present_flip_notify(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc)
330{
331    ScreenPtr                   screen = vblank->screen;
332    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
333
334    DebugPresent(("\tn %" PRIu64 " %p %" PRIu64 " %" PRIu64 ": %08" PRIx32 " -> %08" PRIx32 "\n",
335                  vblank->event_id, vblank, vblank->exec_msc, vblank->target_msc,
336                  vblank->pixmap ? vblank->pixmap->drawable.id : 0,
337                  vblank->window ? vblank->window->drawable.id : 0));
338
339    assert (vblank == screen_priv->flip_pending);
340
341    present_flip_idle(screen);
342
343    xorg_list_del(&vblank->event_queue);
344
345    /* Transfer reference for pixmap and fence from vblank to screen_priv */
346    screen_priv->flip_crtc = vblank->crtc;
347    screen_priv->flip_window = vblank->window;
348    screen_priv->flip_serial = vblank->serial;
349    screen_priv->flip_pixmap = vblank->pixmap;
350    screen_priv->flip_sync = vblank->sync_flip;
351    screen_priv->flip_idle_fence = vblank->idle_fence;
352
353    vblank->pixmap = NULL;
354    vblank->idle_fence = NULL;
355
356    screen_priv->flip_pending = NULL;
357
358    if (vblank->abort_flip)
359        present_unflip(screen);
360
361    present_vblank_notify(vblank, PresentCompleteKindPixmap, PresentCompleteModeFlip, ust, crtc_msc);
362    present_vblank_destroy(vblank);
363
364    present_flip_try_ready(screen);
365}
366
367void
368present_event_notify(uint64_t event_id, uint64_t ust, uint64_t msc)
369{
370    present_vblank_ptr  vblank;
371    int                 s;
372
373    if (!event_id)
374        return;
375    DebugPresent(("\te %" PRIu64 " ust %" PRIu64 " msc %" PRIu64 "\n", event_id, ust, msc));
376    xorg_list_for_each_entry(vblank, &present_exec_queue, event_queue) {
377        int64_t match = event_id - vblank->event_id;
378        if (match == 0) {
379            present_execute(vblank, ust, msc);
380            return;
381        }
382    }
383    xorg_list_for_each_entry(vblank, &present_flip_queue, event_queue) {
384        if (vblank->event_id == event_id) {
385            if (vblank->queued)
386                present_execute(vblank, ust, msc);
387            else
388                present_flip_notify(vblank, ust, msc);
389            return;
390        }
391    }
392
393    for (s = 0; s < screenInfo.numScreens; s++) {
394        ScreenPtr               screen = screenInfo.screens[s];
395        present_screen_priv_ptr screen_priv = present_screen_priv(screen);
396
397        if (event_id == screen_priv->unflip_event_id) {
398            DebugPresent(("\tun %" PRIu64 "\n", event_id));
399            screen_priv->unflip_event_id = 0;
400            present_flip_idle(screen);
401            present_flip_try_ready(screen);
402            return;
403        }
404    }
405}
406
407/*
408 * 'window' is being reconfigured. Check to see if it is involved
409 * in flipping and clean up as necessary
410 */
411static void
412present_check_flip_window (WindowPtr window)
413{
414    ScreenPtr                   screen = window->drawable.pScreen;
415    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
416    present_window_priv_ptr     window_priv = present_window_priv(window);
417    present_vblank_ptr          flip_pending = screen_priv->flip_pending;
418    present_vblank_ptr          vblank;
419    PresentFlipReason           reason;
420
421    /* If this window hasn't ever been used with Present, it can't be
422     * flipping
423     */
424    if (!window_priv)
425        return;
426
427    if (screen_priv->unflip_event_id)
428        return;
429
430    if (flip_pending) {
431        /*
432         * Check pending flip
433         */
434        if (flip_pending->window == window) {
435            if (!present_check_flip(flip_pending->crtc, window, flip_pending->pixmap,
436                                    flip_pending->sync_flip, NULL, 0, 0, NULL))
437                present_set_abort_flip(screen);
438        }
439    } else {
440        /*
441         * Check current flip
442         */
443        if (window == screen_priv->flip_window) {
444            if (!present_check_flip(screen_priv->flip_crtc, window, screen_priv->flip_pixmap, screen_priv->flip_sync, NULL, 0, 0, NULL))
445                present_unflip(screen);
446        }
447    }
448
449    /* Now check any queued vblanks */
450    xorg_list_for_each_entry(vblank, &window_priv->vblank, window_list) {
451        if (vblank->queued && vblank->flip && !present_check_flip(vblank->crtc, window, vblank->pixmap, vblank->sync_flip, NULL, 0, 0, &reason)) {
452            vblank->flip = FALSE;
453            vblank->reason = reason;
454            if (vblank->sync_flip)
455                vblank->exec_msc = vblank->target_msc;
456        }
457    }
458}
459
460static Bool
461present_scmd_can_window_flip(WindowPtr window)
462{
463    ScreenPtr                   screen = window->drawable.pScreen;
464    PixmapPtr                   window_pixmap;
465    WindowPtr                   root = screen->root;
466    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
467
468    if (!screen_priv)
469        return FALSE;
470
471    if (!screen_priv->info)
472        return FALSE;
473
474    /* Check to see if the driver supports flips at all */
475    if (!screen_priv->info->flip)
476        return FALSE;
477
478    /* Make sure the window hasn't been redirected with Composite */
479    window_pixmap = screen->GetWindowPixmap(window);
480    if (window_pixmap != screen->GetScreenPixmap(screen) &&
481        window_pixmap != screen_priv->flip_pixmap &&
482        window_pixmap != present_flip_pending_pixmap(screen))
483        return FALSE;
484
485    /* Check for full-screen window */
486    if (!RegionEqual(&window->clipList, &root->winSize)) {
487        return FALSE;
488    }
489
490    /* Does the window match the pixmap exactly? */
491    if (window->drawable.x != 0 || window->drawable.y != 0) {
492        return FALSE;
493    }
494
495    return TRUE;
496}
497
498/*
499 * Clean up any pending or current flips for this window
500 */
501static void
502present_scmd_clear_window_flip(WindowPtr window)
503{
504    ScreenPtr                   screen = window->drawable.pScreen;
505    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
506    present_vblank_ptr          flip_pending = screen_priv->flip_pending;
507
508    if (flip_pending && flip_pending->window == window) {
509        present_set_abort_flip(screen);
510        flip_pending->window = NULL;
511    }
512    if (screen_priv->flip_window == window) {
513        present_restore_screen_pixmap(screen);
514        screen_priv->flip_window = NULL;
515    }
516}
517
518/*
519 * Once the required MSC has been reached, execute the pending request.
520 *
521 * For requests to actually present something, either blt contents to
522 * the screen or queue a frame buffer swap.
523 *
524 * For requests to just get the current MSC/UST combo, skip that part and
525 * go straight to event delivery
526 */
527
528static void
529present_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc)
530{
531    WindowPtr                   window = vblank->window;
532    ScreenPtr                   screen = window->drawable.pScreen;
533    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
534    if (vblank && vblank->crtc) {
535        screen_priv=present_screen_priv(vblank->crtc->pScreen);
536    }
537
538    if (present_execute_wait(vblank, crtc_msc))
539        return;
540
541    if (vblank->flip && vblank->pixmap && vblank->window) {
542        if (screen_priv->flip_pending || screen_priv->unflip_event_id) {
543            DebugPresent(("\tr %" PRIu64 " %p (pending %p unflip %" PRIu64 ")\n",
544                          vblank->event_id, vblank,
545                          screen_priv->flip_pending, screen_priv->unflip_event_id));
546            xorg_list_del(&vblank->event_queue);
547            xorg_list_append(&vblank->event_queue, &present_flip_queue);
548            vblank->flip_ready = TRUE;
549            return;
550        }
551    }
552
553    xorg_list_del(&vblank->event_queue);
554    xorg_list_del(&vblank->window_list);
555    vblank->queued = FALSE;
556
557    if (vblank->pixmap && vblank->window) {
558
559        if (vblank->flip) {
560
561            DebugPresent(("\tf %" PRIu64 " %p %" PRIu64 ": %08" PRIx32 " -> %08" PRIx32 "\n",
562                          vblank->event_id, vblank, crtc_msc,
563                          vblank->pixmap->drawable.id, vblank->window->drawable.id));
564
565            /* Prepare to flip by placing it in the flip queue and
566             * and sticking it into the flip_pending field
567             */
568            screen_priv->flip_pending = vblank;
569
570            xorg_list_add(&vblank->event_queue, &present_flip_queue);
571            /* Try to flip
572             */
573            if (present_flip(vblank->crtc, vblank->event_id, vblank->target_msc, vblank->pixmap, vblank->sync_flip)) {
574                RegionPtr damage;
575
576                /* Fix window pixmaps:
577                 *  1) Restore previous flip window pixmap
578                 *  2) Set current flip window pixmap to the new pixmap
579                 */
580                if (screen_priv->flip_window && screen_priv->flip_window != window)
581                    present_set_tree_pixmap(screen_priv->flip_window,
582                                            screen_priv->flip_pixmap,
583                                            (*screen->GetScreenPixmap)(screen));
584                present_set_tree_pixmap(vblank->window, NULL, vblank->pixmap);
585                present_set_tree_pixmap(screen->root, NULL, vblank->pixmap);
586
587                /* Report update region as damaged
588                 */
589                if (vblank->update) {
590                    damage = vblank->update;
591                    RegionIntersect(damage, damage, &window->clipList);
592                } else
593                    damage = &window->clipList;
594
595                DamageDamageRegion(&vblank->window->drawable, damage);
596                return;
597            }
598
599            xorg_list_del(&vblank->event_queue);
600            /* Oops, flip failed. Clear the flip_pending field
601              */
602            screen_priv->flip_pending = NULL;
603            vblank->flip = FALSE;
604            vblank->exec_msc = vblank->target_msc;
605        }
606        DebugPresent(("\tc %p %" PRIu64 ": %08" PRIx32 " -> %08" PRIx32 "\n",
607                      vblank, crtc_msc, vblank->pixmap->drawable.id, vblank->window->drawable.id));
608        if (screen_priv->flip_pending) {
609
610            /* Check pending flip
611             */
612            if (window == screen_priv->flip_pending->window)
613                present_set_abort_flip(screen);
614        } else if (!screen_priv->unflip_event_id) {
615
616            /* Check current flip
617             */
618            if (window == screen_priv->flip_window)
619                present_unflip(screen);
620        }
621
622        present_execute_copy(vblank, crtc_msc);
623
624        if (vblank->queued) {
625            xorg_list_add(&vblank->event_queue, &present_exec_queue);
626            xorg_list_append(&vblank->window_list,
627                             &present_get_window_priv(window, TRUE)->vblank);
628            return;
629        }
630    }
631
632    present_execute_post(vblank, ust, crtc_msc);
633}
634
635static void
636present_scmd_update_window_crtc(WindowPtr window, RRCrtcPtr crtc, uint64_t new_msc)
637{
638    present_window_priv_ptr window_priv = present_get_window_priv(window, TRUE);
639    uint64_t                old_ust, old_msc;
640
641    /* Crtc unchanged, no offset. */
642    if (crtc == window_priv->crtc)
643        return;
644
645    /* No crtc earlier to offset against, just set the crtc. */
646    if (window_priv->crtc == PresentCrtcNeverSet) {
647        window_priv->crtc = crtc;
648        return;
649    }
650
651    /* Crtc may have been turned off or be destroyed, just use whatever previous MSC we'd seen from this CRTC. */
652    if (!RRCrtcExists(window->drawable.pScreen, window_priv->crtc) ||
653        present_get_ust_msc(window->drawable.pScreen, window_priv->crtc, &old_ust, &old_msc) != Success)
654        old_msc = window_priv->msc;
655
656    window_priv->msc_offset += new_msc - old_msc;
657    window_priv->crtc = crtc;
658}
659
660static int
661present_scmd_pixmap(WindowPtr window,
662                    PixmapPtr pixmap,
663                    CARD32 serial,
664                    RegionPtr valid,
665                    RegionPtr update,
666                    int16_t x_off,
667                    int16_t y_off,
668                    RRCrtcPtr target_crtc,
669                    SyncFence *wait_fence,
670                    SyncFence *idle_fence,
671                    uint32_t options,
672                    uint64_t target_window_msc,
673                    uint64_t divisor,
674                    uint64_t remainder,
675                    present_notify_ptr notifies,
676                    int num_notifies)
677{
678    uint64_t                    ust = 0;
679    uint64_t                    target_msc;
680    uint64_t                    crtc_msc = 0;
681    int                         ret;
682    present_vblank_ptr          vblank, tmp;
683    ScreenPtr                   screen = window->drawable.pScreen;
684    present_window_priv_ptr     window_priv = present_get_window_priv(window, TRUE);
685    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
686
687    if (!window_priv)
688        return BadAlloc;
689
690    if (!screen_priv || !screen_priv->info)
691        target_crtc = NULL;
692    else if (!target_crtc) {
693        /* Update the CRTC if we have a pixmap or we don't have a CRTC
694         */
695        if (!pixmap)
696            target_crtc = window_priv->crtc;
697
698        if (!target_crtc || target_crtc == PresentCrtcNeverSet)
699            target_crtc = present_get_crtc(window);
700    }
701
702    ret = present_get_ust_msc(screen, target_crtc, &ust, &crtc_msc);
703
704    present_scmd_update_window_crtc(window, target_crtc, crtc_msc);
705
706    if (ret == Success) {
707        /* Stash the current MSC away in case we need it later
708         */
709        window_priv->msc = crtc_msc;
710    }
711
712    target_msc = present_get_target_msc(target_window_msc + window_priv->msc_offset,
713                                        crtc_msc,
714                                        divisor,
715                                        remainder,
716                                        options);
717
718    /*
719     * Look for a matching presentation already on the list and
720     * don't bother doing the previous one if this one will overwrite it
721     * in the same frame
722     */
723
724    if (!update && pixmap) {
725        xorg_list_for_each_entry_safe(vblank, tmp, &window_priv->vblank, window_list) {
726
727            if (!vblank->pixmap)
728                continue;
729
730            if (!vblank->queued)
731                continue;
732
733            if (vblank->crtc != target_crtc || vblank->target_msc != target_msc)
734                continue;
735
736            present_vblank_scrap(vblank);
737            if (vblank->flip_ready)
738                present_re_execute(vblank);
739        }
740    }
741
742    vblank = present_vblank_create(window,
743                                   pixmap,
744                                   serial,
745                                   valid,
746                                   update,
747                                   x_off,
748                                   y_off,
749                                   target_crtc,
750                                   wait_fence,
751                                   idle_fence,
752                                   options,
753                                   screen_priv->info ? screen_priv->info->capabilities : 0,
754                                   notifies,
755                                   num_notifies,
756                                   target_msc,
757                                   crtc_msc);
758
759    if (!vblank)
760        return BadAlloc;
761
762    vblank->event_id = ++present_scmd_event_id;
763
764    if (vblank->flip && vblank->sync_flip)
765        vblank->exec_msc--;
766
767    xorg_list_append(&vblank->event_queue, &present_exec_queue);
768    vblank->queued = TRUE;
769    if (msc_is_after(vblank->exec_msc, crtc_msc)) {
770        ret = present_queue_vblank(screen, window, target_crtc, vblank->event_id, vblank->exec_msc);
771        if (ret == Success)
772            return Success;
773
774        DebugPresent(("present_queue_vblank failed\n"));
775    }
776
777    present_execute(vblank, ust, crtc_msc);
778
779    return Success;
780}
781
782static void
783present_scmd_abort_vblank(ScreenPtr screen, WindowPtr window, RRCrtcPtr crtc, uint64_t event_id, uint64_t msc)
784{
785    present_vblank_ptr  vblank;
786
787    if (crtc == NULL)
788        present_fake_abort_vblank(screen, event_id, msc);
789    else
790    {
791        present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
792
793        (*screen_priv->info->abort_vblank) (crtc, event_id, msc);
794    }
795
796    xorg_list_for_each_entry(vblank, &present_exec_queue, event_queue) {
797        int64_t match = event_id - vblank->event_id;
798        if (match == 0) {
799            xorg_list_del(&vblank->event_queue);
800            vblank->queued = FALSE;
801            return;
802        }
803    }
804    xorg_list_for_each_entry(vblank, &present_flip_queue, event_queue) {
805        if (vblank->event_id == event_id) {
806            xorg_list_del(&vblank->event_queue);
807            vblank->queued = FALSE;
808            return;
809        }
810    }
811}
812
813static void
814present_scmd_flip_destroy(ScreenPtr screen)
815{
816    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
817
818    /* Reset window pixmaps back to the screen pixmap */
819    if (screen_priv->flip_pending)
820        present_set_abort_flip(screen);
821
822    /* Drop reference to any pending flip or unflip pixmaps. */
823    present_flip_idle(screen);
824}
825
826void
827present_scmd_init_mode_hooks(present_screen_priv_ptr screen_priv)
828{
829    screen_priv->query_capabilities =   &present_scmd_query_capabilities;
830    screen_priv->get_crtc           =   &present_scmd_get_crtc;
831
832    screen_priv->check_flip         =   &present_check_flip;
833    screen_priv->check_flip_window  =   &present_check_flip_window;
834    screen_priv->can_window_flip    =   &present_scmd_can_window_flip;
835    screen_priv->clear_window_flip  =   &present_scmd_clear_window_flip;
836
837    screen_priv->present_pixmap     =   &present_scmd_pixmap;
838
839    screen_priv->queue_vblank       =   &present_queue_vblank;
840    screen_priv->flush              =   &present_flush;
841    screen_priv->re_execute         =   &present_re_execute;
842
843    screen_priv->abort_vblank       =   &present_scmd_abort_vblank;
844    screen_priv->flip_destroy       =   &present_scmd_flip_destroy;
845}
846
847Bool
848present_init(void)
849{
850    xorg_list_init(&present_exec_queue);
851    xorg_list_init(&present_flip_queue);
852    present_fake_queue_init();
853    return TRUE;
854}
855