Home | History | Annotate | Line # | Download | only in src
      1 /*
      2  * Copyright (C) 2012 CodeWeavers, Inc.
      3  *
      4  * Permission is hereby granted, free of charge, to any person obtaining a
      5  * copy of this software and associated documentation files (the "Software"),
      6  * to deal in the Software without restriction, including without limitation
      7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      8  * and/or sell copies of the Software, and to permit persons to whom the
      9  * Software is furnished to do so, subject to the following conditions:
     10  *
     11  * The above copyright notice and this permission notice (including the next
     12  * paragraph) shall be included in all copies or substantial portions of the
     13  * Software.
     14  *
     15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     21  * SOFTWARE.
     22  *
     23  * Authors:
     24  *    Jeremy White <jwhite (at) codeweavers.com>
     25  */
     26 
     27 /*----------------------------------------------------------------------------
     28   Deferred Frames Per Second (dfps) Support File
     29 
     30     By default, The xorg-video-qxl driver transmits all of the video
     31   operation across the wire.  While this has the greatest fidelity, and
     32   lends itself well to a variety of optimizations and behavior tuning,
     33   it does not always use bandwidth efficiently.
     34 
     35   This file implements a 'deferred frames' mode which instead renders
     36   everything to a frame buffer, and then periodically sends only updated
     37   portions of the screen.
     38 
     39   For some use cases, this proves to be far more efficient with network
     40   resources.
     41 ----------------------------------------------------------------------------*/
     42 
     43 #include <xorg-server.h>
     44 #include "qxl.h"
     45 #include "dfps.h"
     46 
     47 typedef struct _dfps_info_t
     48 {
     49     RegionRec   updated_region;
     50 
     51     PixmapPtr   copy_src;
     52     Pixel       solid_pixel;
     53     GCPtr       pgc;
     54 } dfps_info_t;
     55 
     56 static void dfps_ticker(void *opaque);
     57 
     58 static inline dfps_info_t *dfps_get_info (PixmapPtr pixmap)
     59 {
     60 #if HAS_DEVPRIVATEKEYREC
     61     return dixGetPrivate(&pixmap->devPrivates, &uxa_pixmap_index);
     62 #else
     63     return dixLookupPrivate(&pixmap->devPrivates, &uxa_pixmap_index);
     64 #endif
     65 }
     66 
     67 static inline void dfps_set_info (PixmapPtr pixmap, dfps_info_t *info)
     68 {
     69     dixSetPrivate(&pixmap->devPrivates, &uxa_pixmap_index, info);
     70 }
     71 typedef struct FrameTimer {
     72     OsTimerPtr xorg_timer;
     73     FrameTimerFunc func;
     74     void *opaque; // also stored in xorg_timer, but needed for timer_start
     75 } Timer;
     76 
     77 static CARD32 xorg_timer_callback(
     78     OsTimerPtr xorg_timer,
     79     CARD32 time,
     80     pointer arg)
     81 {
     82     FrameTimer *timer = (FrameTimer*)arg;
     83 
     84     timer->func(timer->opaque);
     85     return 0; // if non zero xorg does a TimerSet, we don't want that.
     86 }
     87 
     88 static FrameTimer* timer_add(FrameTimerFunc func, void *opaque)
     89 {
     90     FrameTimer *timer = calloc(sizeof(FrameTimer), 1);
     91 
     92     timer->xorg_timer = TimerSet(NULL, 0, 1e9 /* TODO: infinity? */, xorg_timer_callback, timer);
     93     timer->func = func;
     94     timer->opaque = opaque;
     95     return timer;
     96 }
     97 
     98 static void timer_start(FrameTimer *timer, uint32_t ms)
     99 {
    100     TimerSet(timer->xorg_timer, 0 /* flags */, ms, xorg_timer_callback, timer);
    101 }
    102 
    103 void dfps_start_ticker(qxl_screen_t *qxl)
    104 {
    105     qxl->frames_timer = timer_add(dfps_ticker, qxl);
    106     timer_start(qxl->frames_timer, 1000 / qxl->deferred_fps);
    107 }
    108 
    109 static void dfps_ticker(void *opaque)
    110 {
    111     qxl_screen_t *qxl = (qxl_screen_t *) opaque;
    112     dfps_info_t *info = NULL;
    113     PixmapPtr pixmap;
    114 
    115     pixmap = qxl->pScrn->pScreen->GetScreenPixmap(qxl->pScrn->pScreen);
    116     if (pixmap)
    117         info = dfps_get_info(pixmap);
    118     if (info)
    119     {
    120         qxl_surface_upload_primary_regions(qxl, pixmap, &info->updated_region);
    121         RegionUninit(&info->updated_region);
    122         RegionInit(&info->updated_region, NULL, 0);
    123     }
    124     timer_start(qxl->frames_timer, 1000 / qxl->deferred_fps);
    125 }
    126 
    127 
    128 static Bool unaccel (void)
    129 {
    130     return FALSE;
    131 }
    132 
    133 static Bool is_main_pixmap(PixmapPtr pixmap)
    134 {
    135     ScreenPtr screen = pixmap->drawable.pScreen;
    136     if (screen && pixmap == screen->GetScreenPixmap(screen))
    137         return TRUE;
    138     return FALSE;
    139 }
    140 
    141 
    142 /* Establish a maximum number of disparate regions we'll track before we just
    143    treat the entire bounding rectangle as having changed.
    144 
    145    The number 20 seemed intuitive, and also produced the best results in
    146    benchmarking x11perf -circle10 -repeat 1
    147 */
    148 #define DFPS_MAX_UPDATE_REGIONS 20
    149 static void dfps_update_box(RegionPtr dest, int x_1, int x_2, int y_1, int y_2);
    150 
    151 static void dfps_update_region(RegionPtr dest, RegionPtr src)
    152 {
    153     Bool throwaway_bool;
    154 
    155     RegionAppend(dest, src);
    156     RegionValidate(dest, &throwaway_bool);
    157     if (RegionNumRects(dest) > DFPS_MAX_UPDATE_REGIONS)
    158     {
    159         struct pixman_box16 box = * (RegionExtents(dest));
    160         RegionUninit(dest);
    161         RegionInit(dest, NULL, 0);
    162         dfps_update_box(dest, box.x1, box.x2, box.y1, box.y2);
    163     }
    164 }
    165 
    166 static void dfps_update_box(RegionPtr dest, int x_1, int x_2, int y_1, int y_2)
    167 {
    168     struct pixman_box16 box;
    169     RegionPtr region;
    170 
    171     box.x1 = x_1; box.x2 = x_2; box.y1 = y_1; box.y2 = y_2;
    172     region = RegionCreate(&box, 1);
    173 
    174     dfps_update_region(dest, region);
    175 
    176     RegionUninit(region);
    177     RegionDestroy(region);
    178 }
    179 
    180 static Bool dfps_prepare_solid (PixmapPtr pixmap, int alu, Pixel planemask, Pixel fg)
    181 {
    182     dfps_info_t *info;
    183 
    184     if (!(info = dfps_get_info (pixmap)))
    185         return FALSE;
    186 
    187     info->solid_pixel = fg;
    188     info->pgc = GetScratchGC(pixmap->drawable.depth, pixmap->drawable.pScreen);
    189     if (! info->pgc)
    190         return FALSE;
    191 
    192     info->pgc->alu = alu;
    193     info->pgc->planemask = planemask;
    194     info->pgc->fgPixel = fg;
    195     info->pgc->fillStyle = FillSolid;
    196 
    197     fbValidateGC(info->pgc, GCForeground | GCPlaneMask, &pixmap->drawable);
    198 
    199     return TRUE;
    200 }
    201 
    202 static void dfps_solid (PixmapPtr pixmap, int x_1, int y_1, int x_2, int y_2)
    203 {
    204     dfps_info_t *info;
    205 
    206     if (!(info = dfps_get_info (pixmap)))
    207         return;
    208 
    209     /* Draw to the frame buffer */
    210     fbFill(&pixmap->drawable, info->pgc, x_1, y_1, x_2 - x_1, y_2 - y_1);
    211 
    212     /* Track the updated region */
    213     if (is_main_pixmap(pixmap))
    214         dfps_update_box(&info->updated_region, x_1, x_2, y_1, y_2);
    215     return;
    216 }
    217 
    218 static void dfps_done_solid (PixmapPtr pixmap)
    219 {
    220     dfps_info_t *info;
    221 
    222     if ((info = dfps_get_info (pixmap)))
    223     {
    224         FreeScratchGC(info->pgc);
    225         info->pgc = NULL;
    226     }
    227 }
    228 
    229 static Bool dfps_prepare_copy (PixmapPtr source, PixmapPtr dest,
    230                   int xdir, int ydir, int alu,
    231                   Pixel planemask)
    232 {
    233     dfps_info_t *info;
    234 
    235     if (!(info = dfps_get_info (dest)))
    236         return FALSE;
    237 
    238     info->copy_src = source;
    239 
    240     info->pgc = GetScratchGC(dest->drawable.depth, dest->drawable.pScreen);
    241     if (! info->pgc)
    242         return FALSE;
    243 
    244     info->pgc->alu = alu;
    245     info->pgc->planemask = planemask;
    246 
    247     fbValidateGC(info->pgc, GCPlaneMask, &dest->drawable);
    248 
    249     return TRUE;
    250 }
    251 
    252 static void dfps_copy (PixmapPtr dest,
    253           int src_x1, int src_y1,
    254           int dest_x1, int dest_y1,
    255           int width, int height)
    256 {
    257     dfps_info_t *info;
    258 
    259     if (!(info = dfps_get_info (dest)))
    260         return;
    261 
    262     /* Render into to the frame buffer */
    263     fbCopyArea(&info->copy_src->drawable, &dest->drawable, info->pgc, src_x1, src_y1, width, height, dest_x1, dest_y1);
    264 
    265     /* Update the tracking region */
    266     if (is_main_pixmap(dest))
    267         dfps_update_box(&info->updated_region, dest_x1, dest_x1 + width, dest_y1, dest_y1 + height);
    268 }
    269 
    270 static void dfps_done_copy (PixmapPtr dest)
    271 {
    272     dfps_info_t *info;
    273 
    274     if ((info = dfps_get_info (dest)))
    275     {
    276         FreeScratchGC(info->pgc);
    277         info->pgc = NULL;
    278     }
    279 }
    280 
    281 static Bool dfps_put_image (PixmapPtr dest, int x, int y, int w, int h,
    282                char *src, int src_pitch)
    283 {
    284     dfps_info_t *info;
    285     FbBits *dst;
    286     FbStride dst_stride;
    287     int dst_bpp;
    288 
    289     if (!(info = dfps_get_info (dest)))
    290         return FALSE;
    291 
    292     if (is_main_pixmap(dest))
    293         dfps_update_box(&info->updated_region, x, x + w, y, y + h);
    294 
    295     fbPrepareAccess(dest);
    296     fbGetPixmapBitsData(dest, dst, dst_stride, dst_bpp);
    297     fbBlt((FbBits *) src, src_pitch / sizeof(FbStip), 0, dst + (y * dst_stride), dst_stride,
    298            x * dst_bpp, w * dst_bpp, h, GXcopy, FB_ALLONES, dst_bpp, FALSE, FALSE);
    299     fbFinishAccess(dest);
    300 
    301     return TRUE;
    302 }
    303 
    304 
    305 static Bool dfps_prepare_access (PixmapPtr pixmap, RegionPtr region, uxa_access_t requested_access)
    306 {
    307     fbPrepareAccess(pixmap);
    308     if (requested_access == UXA_ACCESS_RW)
    309     {
    310         dfps_info_t *info;
    311 
    312         if (!(info = dfps_get_info (pixmap)))
    313             return FALSE;
    314 
    315         if (is_main_pixmap(pixmap))
    316             dfps_update_region(&info->updated_region, region);
    317     }
    318     return TRUE;
    319 }
    320 
    321 static void dfps_finish_access (PixmapPtr pixmap)
    322 {
    323     fbFinishAccess(pixmap);
    324 }
    325 
    326 static Bool dfps_pixmap_is_offscreen (PixmapPtr pixmap)
    327 {
    328     return !!dfps_get_info(pixmap);
    329 }
    330 
    331 static void dfps_set_screen_pixmap (PixmapPtr pixmap)
    332 {
    333     pixmap->drawable.pScreen->devPrivate = pixmap;
    334 }
    335 
    336 static void dfps_clear_pixmap(PixmapPtr pixmap, int w, int h)
    337 {
    338     GCPtr pgc;
    339     pgc = GetScratchGC(pixmap->drawable.depth, pixmap->drawable.pScreen);
    340     if (pgc)
    341     {
    342         fbFill(&pixmap->drawable, pgc, 0, 0, w, h);
    343         FreeScratchGC(pgc);
    344     }
    345 }
    346 
    347 static PixmapPtr dfps_create_pixmap (ScreenPtr screen, int w, int h, int depth, unsigned usage)
    348 {
    349     PixmapPtr pixmap;
    350     dfps_info_t *info;
    351 
    352     info = calloc(1, sizeof(*info));
    353     if (!info)
    354         return FALSE;
    355     RegionInit(&info->updated_region, NULL, 0);
    356 
    357     pixmap = fbCreatePixmap (screen, w, h, depth, usage);
    358     if (pixmap)
    359     {
    360         dfps_clear_pixmap(pixmap, w, h);
    361         dfps_set_info(pixmap, info);
    362     }
    363     else
    364         free(info);
    365 
    366     return pixmap;
    367 }
    368 
    369 static Bool dfps_destroy_pixmap (PixmapPtr pixmap)
    370 {
    371     if (pixmap->refcnt == 1)
    372     {
    373         dfps_info_t *info = dfps_get_info (pixmap);
    374         if (info)
    375             free(info);
    376         dfps_set_info(pixmap, NULL);
    377     }
    378 
    379     return fbDestroyPixmap (pixmap);
    380 }
    381 
    382 void dfps_set_uxa_functions(qxl_screen_t *qxl, ScreenPtr screen)
    383 {
    384     /* Solid fill */
    385     //qxl->uxa->check_solid = dfps_check_solid;
    386     qxl->uxa->prepare_solid = dfps_prepare_solid;
    387     qxl->uxa->solid = dfps_solid;
    388     qxl->uxa->done_solid = dfps_done_solid;
    389 
    390     /* Copy */
    391     //qxl->uxa->check_copy = qxl_check_copy;
    392     qxl->uxa->prepare_copy = dfps_prepare_copy;
    393     qxl->uxa->copy = dfps_copy;
    394     qxl->uxa->done_copy = dfps_done_copy;
    395 
    396     /* Composite */
    397     qxl->uxa->check_composite = (typeof(qxl->uxa->check_composite))unaccel;
    398     qxl->uxa->check_composite_target = (typeof(qxl->uxa->check_composite_target))unaccel;
    399     qxl->uxa->check_composite_texture = (typeof(qxl->uxa->check_composite_texture))unaccel;
    400     qxl->uxa->prepare_composite = (typeof(qxl->uxa->prepare_composite))unaccel;
    401     qxl->uxa->composite = (typeof(qxl->uxa->composite))unaccel;
    402     qxl->uxa->done_composite = (typeof(qxl->uxa->done_composite))unaccel;
    403 
    404     /* PutImage */
    405     qxl->uxa->put_image = dfps_put_image;
    406 
    407     /* Prepare access */
    408     qxl->uxa->prepare_access = dfps_prepare_access;
    409     qxl->uxa->finish_access = dfps_finish_access;
    410 
    411     /* General screen information */
    412     qxl->uxa->pixmap_is_offscreen = dfps_pixmap_is_offscreen;
    413 
    414     screen->SetScreenPixmap = dfps_set_screen_pixmap;
    415     screen->CreatePixmap = dfps_create_pixmap;
    416     screen->DestroyPixmap = dfps_destroy_pixmap;
    417 }
    418