xserver

xserver with xephyr scale patch
git clone https://git.neptards.moe/u3shit/xserver.git
Log | Files | Refs | README | LICENSE

vblank.c (16766B)


      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 /** @file vblank.c
     24  *
     25  * Support for tracking the DRM's vblank events.
     26  */
     27 
     28 #ifdef HAVE_DIX_CONFIG_H
     29 #include "dix-config.h"
     30 #endif
     31 
     32 #include <unistd.h>
     33 #include <xf86.h>
     34 #include <xf86Crtc.h>
     35 #include "driver.h"
     36 #include "drmmode_display.h"
     37 
     38 /**
     39  * Tracking for outstanding events queued to the kernel.
     40  *
     41  * Each list entry is a struct ms_drm_queue, which has a uint32_t
     42  * value generated from drm_seq that identifies the event and a
     43  * reference back to the crtc/screen associated with the event.  It's
     44  * done this way rather than in the screen because we want to be able
     45  * to drain the list of event handlers that should be called at server
     46  * regen time, even though we don't close the drm fd and have no way
     47  * to actually drain the kernel events.
     48  */
     49 static struct xorg_list ms_drm_queue;
     50 static uint32_t ms_drm_seq;
     51 
     52 static void box_intersect(BoxPtr dest, BoxPtr a, BoxPtr b)
     53 {
     54     dest->x1 = a->x1 > b->x1 ? a->x1 : b->x1;
     55     dest->x2 = a->x2 < b->x2 ? a->x2 : b->x2;
     56     if (dest->x1 >= dest->x2) {
     57         dest->x1 = dest->x2 = dest->y1 = dest->y2 = 0;
     58         return;
     59     }
     60 
     61     dest->y1 = a->y1 > b->y1 ? a->y1 : b->y1;
     62     dest->y2 = a->y2 < b->y2 ? a->y2 : b->y2;
     63     if (dest->y1 >= dest->y2)
     64         dest->x1 = dest->x2 = dest->y1 = dest->y2 = 0;
     65 }
     66 
     67 static void rr_crtc_box(RRCrtcPtr crtc, BoxPtr crtc_box)
     68 {
     69     if (crtc->mode) {
     70         crtc_box->x1 = crtc->x;
     71         crtc_box->y1 = crtc->y;
     72         switch (crtc->rotation) {
     73             case RR_Rotate_0:
     74             case RR_Rotate_180:
     75             default:
     76                 crtc_box->x2 = crtc->x + crtc->mode->mode.width;
     77                 crtc_box->y2 = crtc->y + crtc->mode->mode.height;
     78                 break;
     79             case RR_Rotate_90:
     80             case RR_Rotate_270:
     81                 crtc_box->x2 = crtc->x + crtc->mode->mode.height;
     82                 crtc_box->y2 = crtc->y + crtc->mode->mode.width;
     83                 break;
     84         }
     85     } else
     86         crtc_box->x1 = crtc_box->x2 = crtc_box->y1 = crtc_box->y2 = 0;
     87 }
     88 
     89 static int box_area(BoxPtr box)
     90 {
     91     return (int)(box->x2 - box->x1) * (int)(box->y2 - box->y1);
     92 }
     93 
     94 static Bool rr_crtc_on(RRCrtcPtr crtc, Bool crtc_is_xf86_hint)
     95 {
     96     if (!crtc) {
     97         return FALSE;
     98     }
     99     if (crtc_is_xf86_hint && crtc->devPrivate) {
    100          return xf86_crtc_on(crtc->devPrivate);
    101     } else {
    102         return !!crtc->mode;
    103     }
    104 }
    105 
    106 Bool
    107 xf86_crtc_on(xf86CrtcPtr crtc)
    108 {
    109     drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
    110 
    111     return crtc->enabled && drmmode_crtc->dpms_mode == DPMSModeOn;
    112 }
    113 
    114 
    115 /*
    116  * Return the crtc covering 'box'. If two crtcs cover a portion of
    117  * 'box', then prefer the crtc with greater coverage.
    118  */
    119 static RRCrtcPtr
    120 rr_crtc_covering_box(ScreenPtr pScreen, BoxPtr box, Bool screen_is_xf86_hint)
    121 {
    122     rrScrPrivPtr pScrPriv;
    123     RROutputPtr primary_output;
    124     RRCrtcPtr crtc, best_crtc, primary_crtc;
    125     int coverage, best_coverage;
    126     int c;
    127     BoxRec crtc_box, cover_box;
    128 
    129     best_crtc = NULL;
    130     best_coverage = 0;
    131 
    132     if (!dixPrivateKeyRegistered(rrPrivKey))
    133         return NULL;
    134 
    135     pScrPriv = rrGetScrPriv(pScreen);
    136 
    137     if (!pScrPriv)
    138         return NULL;
    139 
    140     primary_crtc = NULL;
    141     primary_output = RRFirstOutput(pScreen);
    142     if (primary_output)
    143         primary_crtc = primary_output->crtc;
    144 
    145     for (c = 0; c < pScrPriv->numCrtcs; c++) {
    146         crtc = pScrPriv->crtcs[c];
    147 
    148         /* If the CRTC is off, treat it as not covering */
    149         if (!rr_crtc_on(crtc, screen_is_xf86_hint))
    150             continue;
    151 
    152         rr_crtc_box(crtc, &crtc_box);
    153         box_intersect(&cover_box, &crtc_box, box);
    154         coverage = box_area(&cover_box);
    155         if ((coverage > best_coverage) ||
    156             (coverage == best_coverage && crtc == primary_crtc)) {
    157             best_crtc = crtc;
    158             best_coverage = coverage;
    159         }
    160     }
    161 
    162     return best_crtc;
    163 }
    164 
    165 static RRCrtcPtr
    166 rr_crtc_covering_box_on_secondary(ScreenPtr pScreen, BoxPtr box)
    167 {
    168     if (!pScreen->isGPU) {
    169         ScreenPtr secondary;
    170         RRCrtcPtr crtc = NULL;
    171 
    172         xorg_list_for_each_entry(secondary, &pScreen->secondary_list, secondary_head) {
    173             if (!secondary->is_output_secondary)
    174                 continue;
    175 
    176             crtc = rr_crtc_covering_box(secondary, box, FALSE);
    177             if (crtc)
    178                 return crtc;
    179         }
    180     }
    181 
    182     return NULL;
    183 }
    184 
    185 xf86CrtcPtr
    186 ms_dri2_crtc_covering_drawable(DrawablePtr pDraw)
    187 {
    188     ScreenPtr pScreen = pDraw->pScreen;
    189     RRCrtcPtr crtc = NULL;
    190     BoxRec box;
    191 
    192     box.x1 = pDraw->x;
    193     box.y1 = pDraw->y;
    194     box.x2 = box.x1 + pDraw->width;
    195     box.y2 = box.y1 + pDraw->height;
    196 
    197     crtc = rr_crtc_covering_box(pScreen, &box, TRUE);
    198     if (crtc) {
    199         return crtc->devPrivate;
    200     }
    201     return NULL;
    202 }
    203 
    204 RRCrtcPtr
    205 ms_randr_crtc_covering_drawable(DrawablePtr pDraw)
    206 {
    207     ScreenPtr pScreen = pDraw->pScreen;
    208     RRCrtcPtr crtc = NULL;
    209     BoxRec box;
    210 
    211     box.x1 = pDraw->x;
    212     box.y1 = pDraw->y;
    213     box.x2 = box.x1 + pDraw->width;
    214     box.y2 = box.y1 + pDraw->height;
    215 
    216     crtc = rr_crtc_covering_box(pScreen, &box, TRUE);
    217     if (!crtc) {
    218         crtc = rr_crtc_covering_box_on_secondary(pScreen, &box);
    219     }
    220     return crtc;
    221 }
    222 
    223 static Bool
    224 ms_get_kernel_ust_msc(xf86CrtcPtr crtc,
    225                       uint64_t *msc, uint64_t *ust)
    226 {
    227     ScreenPtr screen = crtc->randr_crtc->pScreen;
    228     ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
    229     modesettingPtr ms = modesettingPTR(scrn);
    230     drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
    231     drmVBlank vbl;
    232     int ret;
    233 
    234     if (ms->has_queue_sequence || !ms->tried_queue_sequence) {
    235         uint64_t ns;
    236         ms->tried_queue_sequence = TRUE;
    237 
    238         ret = drmCrtcGetSequence(ms->fd, drmmode_crtc->mode_crtc->crtc_id,
    239                                  msc, &ns);
    240         if (ret != -1 || (errno != ENOTTY && errno != EINVAL)) {
    241             ms->has_queue_sequence = TRUE;
    242             if (ret == 0)
    243                 *ust = ns / 1000;
    244             return ret == 0;
    245         }
    246     }
    247     /* Get current count */
    248     vbl.request.type = DRM_VBLANK_RELATIVE | drmmode_crtc->vblank_pipe;
    249     vbl.request.sequence = 0;
    250     vbl.request.signal = 0;
    251     ret = drmWaitVBlank(ms->fd, &vbl);
    252     if (ret) {
    253         *msc = 0;
    254         *ust = 0;
    255         return FALSE;
    256     } else {
    257         *msc = vbl.reply.sequence;
    258         *ust = (CARD64) vbl.reply.tval_sec * 1000000 + vbl.reply.tval_usec;
    259         return TRUE;
    260     }
    261 }
    262 
    263 Bool
    264 ms_queue_vblank(xf86CrtcPtr crtc, ms_queue_flag flags,
    265                 uint64_t msc, uint64_t *msc_queued, uint32_t seq)
    266 {
    267     ScreenPtr screen = crtc->randr_crtc->pScreen;
    268     ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
    269     modesettingPtr ms = modesettingPTR(scrn);
    270     drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
    271     drmVBlank vbl;
    272     int ret;
    273 
    274     for (;;) {
    275         /* Queue an event at the specified sequence */
    276         if (ms->has_queue_sequence || !ms->tried_queue_sequence) {
    277             uint32_t drm_flags = 0;
    278             uint64_t kernel_queued;
    279 
    280             ms->tried_queue_sequence = TRUE;
    281 
    282             if (flags & MS_QUEUE_RELATIVE)
    283                 drm_flags |= DRM_CRTC_SEQUENCE_RELATIVE;
    284             if (flags & MS_QUEUE_NEXT_ON_MISS)
    285                 drm_flags |= DRM_CRTC_SEQUENCE_NEXT_ON_MISS;
    286 
    287             ret = drmCrtcQueueSequence(ms->fd, drmmode_crtc->mode_crtc->crtc_id,
    288                                        drm_flags, msc, &kernel_queued, seq);
    289             if (ret == 0) {
    290                 if (msc_queued)
    291                     *msc_queued = ms_kernel_msc_to_crtc_msc(crtc, kernel_queued, TRUE);
    292                 ms->has_queue_sequence = TRUE;
    293                 return TRUE;
    294             }
    295 
    296             if (ret != -1 || (errno != ENOTTY && errno != EINVAL)) {
    297                 ms->has_queue_sequence = TRUE;
    298                 goto check;
    299             }
    300         }
    301         vbl.request.type = DRM_VBLANK_EVENT | drmmode_crtc->vblank_pipe;
    302         if (flags & MS_QUEUE_RELATIVE)
    303             vbl.request.type |= DRM_VBLANK_RELATIVE;
    304         else
    305             vbl.request.type |= DRM_VBLANK_ABSOLUTE;
    306         if (flags & MS_QUEUE_NEXT_ON_MISS)
    307             vbl.request.type |= DRM_VBLANK_NEXTONMISS;
    308 
    309         vbl.request.sequence = msc;
    310         vbl.request.signal = seq;
    311         ret = drmWaitVBlank(ms->fd, &vbl);
    312         if (ret == 0) {
    313             if (msc_queued)
    314                 *msc_queued = ms_kernel_msc_to_crtc_msc(crtc, vbl.reply.sequence, FALSE);
    315             return TRUE;
    316         }
    317     check:
    318         if (errno != EBUSY) {
    319             ms_drm_abort_seq(scrn, seq);
    320             return FALSE;
    321         }
    322         ms_flush_drm_events(screen);
    323     }
    324 }
    325 
    326 /**
    327  * Convert a 32-bit or 64-bit kernel MSC sequence number to a 64-bit local
    328  * sequence number, adding in the high 32 bits, and dealing with 32-bit
    329  * wrapping if needed.
    330  */
    331 uint64_t
    332 ms_kernel_msc_to_crtc_msc(xf86CrtcPtr crtc, uint64_t sequence, Bool is64bit)
    333 {
    334     drmmode_crtc_private_rec *drmmode_crtc = crtc->driver_private;
    335 
    336     if (!is64bit) {
    337         /* sequence is provided as a 32 bit value from one of the 32 bit apis,
    338          * e.g., drmWaitVBlank(), classic vblank events, or pageflip events.
    339          *
    340          * Track and handle 32-Bit wrapping, somewhat robust against occasional
    341          * out-of-order not always monotonically increasing sequence values.
    342          */
    343         if ((int64_t) sequence < ((int64_t) drmmode_crtc->msc_prev - 0x40000000))
    344             drmmode_crtc->msc_high += 0x100000000L;
    345 
    346         if ((int64_t) sequence > ((int64_t) drmmode_crtc->msc_prev + 0x40000000))
    347             drmmode_crtc->msc_high -= 0x100000000L;
    348 
    349         drmmode_crtc->msc_prev = sequence;
    350 
    351         return drmmode_crtc->msc_high + sequence;
    352     }
    353 
    354     /* True 64-Bit sequence from Linux 4.15+ 64-Bit drmCrtcGetSequence /
    355      * drmCrtcQueueSequence apis and events. Pass through sequence unmodified,
    356      * but update the 32-bit tracking variables with reliable ground truth.
    357      *
    358      * With 64-Bit api in use, the only !is64bit input is from pageflip events,
    359      * and any pageflip event is usually preceded by some is64bit input from
    360      * swap scheduling, so this should provide reliable mapping for pageflip
    361      * events based on true 64-bit input as baseline as well.
    362      */
    363     drmmode_crtc->msc_prev = sequence;
    364     drmmode_crtc->msc_high = sequence & 0xffffffff00000000;
    365 
    366     return sequence;
    367 }
    368 
    369 int
    370 ms_get_crtc_ust_msc(xf86CrtcPtr crtc, CARD64 *ust, CARD64 *msc)
    371 {
    372     ScreenPtr screen = crtc->randr_crtc->pScreen;
    373     ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
    374     modesettingPtr ms = modesettingPTR(scrn);
    375     uint64_t kernel_msc;
    376 
    377     if (!ms_get_kernel_ust_msc(crtc, &kernel_msc, ust))
    378         return BadMatch;
    379     *msc = ms_kernel_msc_to_crtc_msc(crtc, kernel_msc, ms->has_queue_sequence);
    380 
    381     return Success;
    382 }
    383 
    384 /**
    385  * Check for pending DRM events and process them.
    386  */
    387 static void
    388 ms_drm_socket_handler(int fd, int ready, void *data)
    389 {
    390     ScreenPtr screen = data;
    391     ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
    392     modesettingPtr ms = modesettingPTR(scrn);
    393 
    394     if (data == NULL)
    395         return;
    396 
    397     drmHandleEvent(fd, &ms->event_context);
    398 }
    399 
    400 /*
    401  * Enqueue a potential drm response; when the associated response
    402  * appears, we've got data to pass to the handler from here
    403  */
    404 uint32_t
    405 ms_drm_queue_alloc(xf86CrtcPtr crtc,
    406                    void *data,
    407                    ms_drm_handler_proc handler,
    408                    ms_drm_abort_proc abort)
    409 {
    410     ScreenPtr screen = crtc->randr_crtc->pScreen;
    411     ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
    412     struct ms_drm_queue *q;
    413 
    414     q = calloc(1, sizeof(struct ms_drm_queue));
    415 
    416     if (!q)
    417         return 0;
    418     if (!ms_drm_seq)
    419         ++ms_drm_seq;
    420     q->seq = ms_drm_seq++;
    421     q->scrn = scrn;
    422     q->crtc = crtc;
    423     q->data = data;
    424     q->handler = handler;
    425     q->abort = abort;
    426 
    427     xorg_list_add(&q->list, &ms_drm_queue);
    428 
    429     return q->seq;
    430 }
    431 
    432 /**
    433  * Abort one queued DRM entry, removing it
    434  * from the list, calling the abort function and
    435  * freeing the memory
    436  */
    437 static void
    438 ms_drm_abort_one(struct ms_drm_queue *q)
    439 {
    440         xorg_list_del(&q->list);
    441         q->abort(q->data);
    442         free(q);
    443 }
    444 
    445 /**
    446  * Abort all queued entries on a specific scrn, used
    447  * when resetting the X server
    448  */
    449 static void
    450 ms_drm_abort_scrn(ScrnInfoPtr scrn)
    451 {
    452     struct ms_drm_queue *q, *tmp;
    453 
    454     xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) {
    455         if (q->scrn == scrn)
    456             ms_drm_abort_one(q);
    457     }
    458 }
    459 
    460 /**
    461  * Abort by drm queue sequence number.
    462  */
    463 void
    464 ms_drm_abort_seq(ScrnInfoPtr scrn, uint32_t seq)
    465 {
    466     struct ms_drm_queue *q, *tmp;
    467 
    468     xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) {
    469         if (q->seq == seq) {
    470             ms_drm_abort_one(q);
    471             break;
    472         }
    473     }
    474 }
    475 
    476 /*
    477  * Externally usable abort function that uses a callback to match a single
    478  * queued entry to abort
    479  */
    480 void
    481 ms_drm_abort(ScrnInfoPtr scrn, Bool (*match)(void *data, void *match_data),
    482              void *match_data)
    483 {
    484     struct ms_drm_queue *q;
    485 
    486     xorg_list_for_each_entry(q, &ms_drm_queue, list) {
    487         if (match(q->data, match_data)) {
    488             ms_drm_abort_one(q);
    489             break;
    490         }
    491     }
    492 }
    493 
    494 /*
    495  * General DRM kernel handler. Looks for the matching sequence number in the
    496  * drm event queue and calls the handler for it.
    497  */
    498 static void
    499 ms_drm_sequence_handler(int fd, uint64_t frame, uint64_t ns, Bool is64bit, uint64_t user_data)
    500 {
    501     struct ms_drm_queue *q, *tmp;
    502     uint32_t seq = (uint32_t) user_data;
    503 
    504     xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) {
    505         if (q->seq == seq) {
    506             uint64_t msc;
    507 
    508             msc = ms_kernel_msc_to_crtc_msc(q->crtc, frame, is64bit);
    509             xorg_list_del(&q->list);
    510             q->handler(msc, ns / 1000, q->data);
    511             free(q);
    512             break;
    513         }
    514     }
    515 }
    516 
    517 static void
    518 ms_drm_sequence_handler_64bit(int fd, uint64_t frame, uint64_t ns, uint64_t user_data)
    519 {
    520     /* frame is true 64 bit wrapped into 64 bit */
    521     ms_drm_sequence_handler(fd, frame, ns, TRUE, user_data);
    522 }
    523 
    524 static void
    525 ms_drm_handler(int fd, uint32_t frame, uint32_t sec, uint32_t usec,
    526                void *user_ptr)
    527 {
    528     /* frame is 32 bit wrapped into 64 bit */
    529     ms_drm_sequence_handler(fd, frame, ((uint64_t) sec * 1000000 + usec) * 1000,
    530                             FALSE, (uint32_t) (uintptr_t) user_ptr);
    531 }
    532 
    533 Bool
    534 ms_vblank_screen_init(ScreenPtr screen)
    535 {
    536     ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
    537     modesettingPtr ms = modesettingPTR(scrn);
    538     modesettingEntPtr ms_ent = ms_ent_priv(scrn);
    539     xorg_list_init(&ms_drm_queue);
    540 
    541     ms->event_context.version = 4;
    542     ms->event_context.vblank_handler = ms_drm_handler;
    543     ms->event_context.page_flip_handler = ms_drm_handler;
    544     ms->event_context.sequence_handler = ms_drm_sequence_handler_64bit;
    545 
    546     /* We need to re-register the DRM fd for the synchronisation
    547      * feedback on every server generation, so perform the
    548      * registration within ScreenInit and not PreInit.
    549      */
    550     if (ms_ent->fd_wakeup_registered != serverGeneration) {
    551         SetNotifyFd(ms->fd, ms_drm_socket_handler, X_NOTIFY_READ, screen);
    552         ms_ent->fd_wakeup_registered = serverGeneration;
    553         ms_ent->fd_wakeup_ref = 1;
    554     } else
    555         ms_ent->fd_wakeup_ref++;
    556 
    557     return TRUE;
    558 }
    559 
    560 void
    561 ms_vblank_close_screen(ScreenPtr screen)
    562 {
    563     ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
    564     modesettingPtr ms = modesettingPTR(scrn);
    565     modesettingEntPtr ms_ent = ms_ent_priv(scrn);
    566 
    567     ms_drm_abort_scrn(scrn);
    568 
    569     if (ms_ent->fd_wakeup_registered == serverGeneration &&
    570         !--ms_ent->fd_wakeup_ref) {
    571         RemoveNotifyFd(ms->fd);
    572     }
    573 }