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