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