xserver

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

pageflip.c (15828B)


      1 /*
      2  * Copyright © 2014 Intel Corporation
      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_DIX_CONFIG_H
     24 #include "dix-config.h"
     25 #endif
     26 
     27 #include <xserver_poll.h>
     28 #include <xf86drm.h>
     29 
     30 #include "driver.h"
     31 
     32 /*
     33  * Flush the DRM event queue when full; makes space for new events.
     34  *
     35  * Returns a negative value on error, 0 if there was nothing to process,
     36  * or 1 if we handled any events.
     37  */
     38 int
     39 ms_flush_drm_events(ScreenPtr screen)
     40 {
     41     ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
     42     modesettingPtr ms = modesettingPTR(scrn);
     43 
     44     struct pollfd p = { .fd = ms->fd, .events = POLLIN };
     45     int r;
     46 
     47     do {
     48             r = xserver_poll(&p, 1, 0);
     49     } while (r == -1 && (errno == EINTR || errno == EAGAIN));
     50 
     51     /* If there was an error, r will be < 0.  Return that.  If there was
     52      * nothing to process, r == 0.  Return that.
     53      */
     54     if (r <= 0)
     55         return r;
     56 
     57     /* Try to handle the event.  If there was an error, return it. */
     58     r = drmHandleEvent(ms->fd, &ms->event_context);
     59     if (r < 0)
     60         return r;
     61 
     62     /* Otherwise return 1 to indicate that we handled an event. */
     63     return 1;
     64 }
     65 
     66 #ifdef GLAMOR_HAS_GBM
     67 
     68 /*
     69  * Event data for an in progress flip.
     70  * This contains a pointer to the vblank event,
     71  * and information about the flip in progress.
     72  * a reference to this is stored in the per-crtc
     73  * flips.
     74  */
     75 struct ms_flipdata {
     76     ScreenPtr screen;
     77     void *event;
     78     ms_pageflip_handler_proc event_handler;
     79     ms_pageflip_abort_proc abort_handler;
     80     /* number of CRTC events referencing this */
     81     int flip_count;
     82     uint64_t fe_msc;
     83     uint64_t fe_usec;
     84     uint32_t old_fb_id;
     85 };
     86 
     87 /*
     88  * Per crtc pageflipping information,
     89  * These are submitted to the queuing code
     90  * one of them per crtc per flip.
     91  */
     92 struct ms_crtc_pageflip {
     93     Bool on_reference_crtc;
     94     /* reference to the ms_flipdata */
     95     struct ms_flipdata *flipdata;
     96 };
     97 
     98 /**
     99  * Free an ms_crtc_pageflip.
    100  *
    101  * Drops the reference count on the flipdata.
    102  */
    103 static void
    104 ms_pageflip_free(struct ms_crtc_pageflip *flip)
    105 {
    106     struct ms_flipdata *flipdata = flip->flipdata;
    107 
    108     free(flip);
    109     if (--flipdata->flip_count > 0)
    110         return;
    111     free(flipdata);
    112 }
    113 
    114 /**
    115  * Callback for the DRM event queue when a single flip has completed
    116  *
    117  * Once the flip has been completed on all pipes, notify the
    118  * extension code telling it when that happened
    119  */
    120 static void
    121 ms_pageflip_handler(uint64_t msc, uint64_t ust, void *data)
    122 {
    123     struct ms_crtc_pageflip *flip = data;
    124     struct ms_flipdata *flipdata = flip->flipdata;
    125     ScreenPtr screen = flipdata->screen;
    126     ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
    127     modesettingPtr ms = modesettingPTR(scrn);
    128 
    129     if (flip->on_reference_crtc) {
    130         flipdata->fe_msc = msc;
    131         flipdata->fe_usec = ust;
    132     }
    133 
    134     if (flipdata->flip_count == 1) {
    135         flipdata->event_handler(ms, flipdata->fe_msc,
    136                                 flipdata->fe_usec,
    137                                 flipdata->event);
    138 
    139         drmModeRmFB(ms->fd, flipdata->old_fb_id);
    140     }
    141     ms_pageflip_free(flip);
    142 }
    143 
    144 /*
    145  * Callback for the DRM queue abort code.  A flip has been aborted.
    146  */
    147 static void
    148 ms_pageflip_abort(void *data)
    149 {
    150     struct ms_crtc_pageflip *flip = data;
    151     struct ms_flipdata *flipdata = flip->flipdata;
    152     ScreenPtr screen = flipdata->screen;
    153     ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
    154     modesettingPtr ms = modesettingPTR(scrn);
    155 
    156     if (flipdata->flip_count == 1)
    157         flipdata->abort_handler(ms, flipdata->event);
    158 
    159     ms_pageflip_free(flip);
    160 }
    161 
    162 static Bool
    163 do_queue_flip_on_crtc(modesettingPtr ms, xf86CrtcPtr crtc,
    164                       uint32_t flags, uint32_t seq)
    165 {
    166     return drmmode_crtc_flip(crtc, ms->drmmode.fb_id, flags,
    167                              (void *) (uintptr_t) seq);
    168 }
    169 
    170 enum queue_flip_status {
    171     QUEUE_FLIP_SUCCESS,
    172     QUEUE_FLIP_ALLOC_FAILED,
    173     QUEUE_FLIP_QUEUE_ALLOC_FAILED,
    174     QUEUE_FLIP_DRM_FLUSH_FAILED,
    175 };
    176 
    177 static int
    178 queue_flip_on_crtc(ScreenPtr screen, xf86CrtcPtr crtc,
    179                    struct ms_flipdata *flipdata,
    180                    int ref_crtc_vblank_pipe, uint32_t flags)
    181 {
    182     ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
    183     modesettingPtr ms = modesettingPTR(scrn);
    184     drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
    185     struct ms_crtc_pageflip *flip;
    186     uint32_t seq;
    187 
    188     flip = calloc(1, sizeof(struct ms_crtc_pageflip));
    189     if (flip == NULL) {
    190         return QUEUE_FLIP_ALLOC_FAILED;
    191     }
    192 
    193     /* Only the reference crtc will finally deliver its page flip
    194      * completion event. All other crtc's events will be discarded.
    195      */
    196     flip->on_reference_crtc = (drmmode_crtc->vblank_pipe == ref_crtc_vblank_pipe);
    197     flip->flipdata = flipdata;
    198 
    199     seq = ms_drm_queue_alloc(crtc, flip, ms_pageflip_handler, ms_pageflip_abort);
    200     if (!seq) {
    201         free(flip);
    202         return QUEUE_FLIP_QUEUE_ALLOC_FAILED;
    203     }
    204 
    205     /* take a reference on flipdata for use in flip */
    206     flipdata->flip_count++;
    207 
    208     while (do_queue_flip_on_crtc(ms, crtc, flags, seq)) {
    209         /* We may have failed because the event queue was full.  Flush it
    210          * and retry.  If there was nothing to flush, then we failed for
    211          * some other reason and should just return an error.
    212          */
    213         if (ms_flush_drm_events(screen) <= 0) {
    214             /* Aborting will also decrement flip_count and free(flip). */
    215             ms_drm_abort_seq(scrn, seq);
    216             return QUEUE_FLIP_DRM_FLUSH_FAILED;
    217         }
    218 
    219         /* We flushed some events, so try again. */
    220         xf86DrvMsg(scrn->scrnIndex, X_WARNING, "flip queue retry\n");
    221     }
    222 
    223     /* The page flip succeeded. */
    224     return QUEUE_FLIP_SUCCESS;
    225 }
    226 
    227 
    228 #define MS_ASYNC_FLIP_LOG_ENABLE_LOGS_INTERVAL_MS 10000
    229 #define MS_ASYNC_FLIP_LOG_FREQUENT_LOGS_INTERVAL_MS 1000
    230 #define MS_ASYNC_FLIP_FREQUENT_LOG_COUNT 10
    231 
    232 static void
    233 ms_print_pageflip_error(int screen_index, const char *log_prefix,
    234                         int crtc_index, int flags, int err)
    235 {
    236     /* In certain circumstances we will have a lot of flip errors without a
    237      * reasonable way to prevent them. In such case we reduce the number of
    238      * logged messages to at least not fill the error logs.
    239      *
    240      * The details are as follows:
    241      *
    242      * At least on i915 hardware support for async page flip support depends
    243      * on the used modifiers which themselves can change dynamically for a
    244      * screen. This results in the following problems:
    245      *
    246      *  - We can't know about whether a particular CRTC will be able to do an
    247      *    async flip without hardcoding the same logic as the kernel as there's
    248      *    no interface to query this information.
    249      *
    250      *  - There is no way to give this information to an application, because
    251      *    the protocol of the present extension does not specify anything about
    252      *    changing of the capabilities on runtime or the need to re-query them.
    253      *
    254      * Even if the above was solved, the only benefit would be avoiding a
    255      * roundtrip to the kernel and reduced amount of error logs. The former
    256      * does not seem to be a good enough benefit compared to the amount of work
    257      * that would need to be done. The latter is solved below. */
    258 
    259     static CARD32 error_last_time_ms;
    260     static int frequent_logs;
    261     static Bool logs_disabled;
    262 
    263     if (flags & DRM_MODE_PAGE_FLIP_ASYNC) {
    264         CARD32 curr_time_ms = GetTimeInMillis();
    265         int clocks_since_last_log = curr_time_ms - error_last_time_ms;
    266 
    267         if (clocks_since_last_log >
    268                 MS_ASYNC_FLIP_LOG_ENABLE_LOGS_INTERVAL_MS) {
    269             frequent_logs = 0;
    270             logs_disabled = FALSE;
    271         }
    272         if (!logs_disabled) {
    273             if (clocks_since_last_log <
    274                     MS_ASYNC_FLIP_LOG_FREQUENT_LOGS_INTERVAL_MS) {
    275                 frequent_logs++;
    276             }
    277 
    278             if (frequent_logs > MS_ASYNC_FLIP_FREQUENT_LOG_COUNT) {
    279                 xf86DrvMsg(screen_index, X_WARNING,
    280                            "%s: detected too frequent flip errors, disabling "
    281                            "logs until frequency is reduced\n", log_prefix);
    282                 logs_disabled = TRUE;
    283             } else {
    284                 xf86DrvMsg(screen_index, X_WARNING,
    285                            "%s: queue async flip during flip on CRTC %d failed: %s\n",
    286                            log_prefix, crtc_index, strerror(err));
    287             }
    288         }
    289         error_last_time_ms = curr_time_ms;
    290     } else {
    291         xf86DrvMsg(screen_index, X_WARNING,
    292                    "%s: queue flip during flip on CRTC %d failed: %s\n",
    293                    log_prefix, crtc_index, strerror(err));
    294     }
    295 }
    296 
    297 
    298 Bool
    299 ms_do_pageflip(ScreenPtr screen,
    300                PixmapPtr new_front,
    301                void *event,
    302                int ref_crtc_vblank_pipe,
    303                Bool async,
    304                ms_pageflip_handler_proc pageflip_handler,
    305                ms_pageflip_abort_proc pageflip_abort,
    306                const char *log_prefix)
    307 {
    308 #ifndef GLAMOR_HAS_GBM
    309     return FALSE;
    310 #else
    311     ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
    312     modesettingPtr ms = modesettingPTR(scrn);
    313     xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
    314     drmmode_bo new_front_bo;
    315     uint32_t flags;
    316     int i;
    317     struct ms_flipdata *flipdata;
    318     ms->glamor.block_handler(screen);
    319 
    320     new_front_bo.gbm = ms->glamor.gbm_bo_from_pixmap(screen, new_front);
    321     new_front_bo.dumb = NULL;
    322 
    323     if (!new_front_bo.gbm) {
    324         xf86DrvMsg(scrn->scrnIndex, X_ERROR,
    325                    "%s: Failed to get GBM BO for flip to new front.\n",
    326                    log_prefix);
    327         return FALSE;
    328     }
    329 
    330     flipdata = calloc(1, sizeof(struct ms_flipdata));
    331     if (!flipdata) {
    332         drmmode_bo_destroy(&ms->drmmode, &new_front_bo);
    333         xf86DrvMsg(scrn->scrnIndex, X_ERROR,
    334                    "%s: Failed to allocate flipdata.\n", log_prefix);
    335         return FALSE;
    336     }
    337 
    338     flipdata->event = event;
    339     flipdata->screen = screen;
    340     flipdata->event_handler = pageflip_handler;
    341     flipdata->abort_handler = pageflip_abort;
    342 
    343     /*
    344      * Take a local reference on flipdata.
    345      * if the first flip fails, the sequence abort
    346      * code will free the crtc flip data, and drop
    347      * its reference which would cause this to be
    348      * freed when we still required it.
    349      */
    350     flipdata->flip_count++;
    351 
    352     /* Create a new handle for the back buffer */
    353     flipdata->old_fb_id = ms->drmmode.fb_id;
    354 
    355     new_front_bo.width = new_front->drawable.width;
    356     new_front_bo.height = new_front->drawable.height;
    357     if (drmmode_bo_import(&ms->drmmode, &new_front_bo,
    358                           &ms->drmmode.fb_id)) {
    359         if (!ms->drmmode.flip_bo_import_failed) {
    360             xf86DrvMsg(scrn->scrnIndex, X_WARNING, "%s: Import BO failed: %s\n",
    361                        log_prefix, strerror(errno));
    362             ms->drmmode.flip_bo_import_failed = TRUE;
    363         }
    364         goto error_out;
    365     } else {
    366         if (ms->drmmode.flip_bo_import_failed &&
    367             new_front != screen->GetScreenPixmap(screen))
    368             ms->drmmode.flip_bo_import_failed = FALSE;
    369     }
    370 
    371     /* Queue flips on all enabled CRTCs.
    372      *
    373      * Note that if/when we get per-CRTC buffers, we'll have to update this.
    374      * Right now it assumes a single shared fb across all CRTCs, with the
    375      * kernel fixing up the offset of each CRTC as necessary.
    376      *
    377      * Also, flips queued on disabled or incorrectly configured displays
    378      * may never complete; this is a configuration error.
    379      */
    380     for (i = 0; i < config->num_crtc; i++) {
    381         enum queue_flip_status flip_status;
    382         xf86CrtcPtr crtc = config->crtc[i];
    383         drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
    384 
    385         if (!xf86_crtc_on(crtc))
    386             continue;
    387 
    388         flags = DRM_MODE_PAGE_FLIP_EVENT;
    389         if (ms->drmmode.can_async_flip && async)
    390             flags |= DRM_MODE_PAGE_FLIP_ASYNC;
    391 
    392         /*
    393          * If this is not the reference crtc used for flip timing and flip event
    394          * delivery and timestamping, ie. not the one whose presentation timing
    395          * we do really care about, and async flips are possible, and requested
    396          * by an xorg.conf option, then we flip this "secondary" crtc without
    397          * sync to vblank. This may cause tearing on such "secondary" outputs,
    398          * but it will prevent throttling of multi-display flips to the refresh
    399          * cycle of any of the secondary crtcs, avoiding periodic slowdowns and
    400          * judder caused by unsynchronized outputs. This is especially useful for
    401          * outputs in a "clone-mode" or "mirror-mode" configuration.
    402          */
    403         if (ms->drmmode.can_async_flip && ms->drmmode.async_flip_secondaries &&
    404             (drmmode_crtc->vblank_pipe != ref_crtc_vblank_pipe) &&
    405             (ref_crtc_vblank_pipe >= 0))
    406             flags |= DRM_MODE_PAGE_FLIP_ASYNC;
    407 
    408         flip_status = queue_flip_on_crtc(screen, crtc, flipdata,
    409                                          ref_crtc_vblank_pipe,
    410                                          flags);
    411 
    412         switch (flip_status) {
    413             case QUEUE_FLIP_ALLOC_FAILED:
    414                 xf86DrvMsg(scrn->scrnIndex, X_WARNING,
    415                            "%s: carrier alloc for queue flip on CRTC %d failed.\n",
    416                            log_prefix, i);
    417                 goto error_undo;
    418             case QUEUE_FLIP_QUEUE_ALLOC_FAILED:
    419                 xf86DrvMsg(scrn->scrnIndex, X_WARNING,
    420                            "%s: entry alloc for queue flip on CRTC %d failed.\n",
    421                            log_prefix, i);
    422                 goto error_undo;
    423             case QUEUE_FLIP_DRM_FLUSH_FAILED:
    424                 ms_print_pageflip_error(scrn->scrnIndex, log_prefix, i, flags, errno);
    425                 goto error_undo;
    426             case QUEUE_FLIP_SUCCESS:
    427                 break;
    428         }
    429     }
    430 
    431     drmmode_bo_destroy(&ms->drmmode, &new_front_bo);
    432 
    433     /*
    434      * Do we have more than our local reference,
    435      * if so and no errors, then drop our local
    436      * reference and return now.
    437      */
    438     if (flipdata->flip_count > 1) {
    439         flipdata->flip_count--;
    440         return TRUE;
    441     }
    442 
    443 error_undo:
    444 
    445     /*
    446      * Have we just got the local reference?
    447      * free the framebuffer if so since nobody successfully
    448      * submitted anything
    449      */
    450     if (flipdata->flip_count == 1) {
    451         drmModeRmFB(ms->fd, ms->drmmode.fb_id);
    452         ms->drmmode.fb_id = flipdata->old_fb_id;
    453     }
    454 
    455 error_out:
    456     drmmode_bo_destroy(&ms->drmmode, &new_front_bo);
    457     /* if only the local reference - free the structure,
    458      * else drop the local reference and return */
    459     if (flipdata->flip_count == 1)
    460         free(flipdata);
    461     else
    462         flipdata->flip_count--;
    463 
    464     return FALSE;
    465 #endif /* GLAMOR_HAS_GBM */
    466 }
    467 
    468 #endif