dfps.c revision d514b0f3
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@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 47typedef 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 56static void dfps_ticker(void *opaque); 57 58static 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 67static inline void dfps_set_info (PixmapPtr pixmap, dfps_info_t *info) 68{ 69 dixSetPrivate(&pixmap->devPrivates, &uxa_pixmap_index, info); 70} 71typedef 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 77static 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 88static 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 98static void timer_start(FrameTimer *timer, uint32_t ms) 99{ 100 TimerSet(timer->xorg_timer, 0 /* flags */, ms, xorg_timer_callback, timer); 101} 102 103void 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 109static 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 128static Bool unaccel (void) 129{ 130 return FALSE; 131} 132 133static 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 149static void dfps_update_box(RegionPtr dest, int x_1, int x_2, int y_1, int y_2); 150 151static 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 166static 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 180static 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 202static 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 218static 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 229static 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 252static 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 270static 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 281static 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 305static 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 321static void dfps_finish_access (PixmapPtr pixmap) 322{ 323 fbFinishAccess(pixmap); 324} 325 326static Bool dfps_pixmap_is_offscreen (PixmapPtr pixmap) 327{ 328 return !!dfps_get_info(pixmap); 329} 330 331static void dfps_set_screen_pixmap (PixmapPtr pixmap) 332{ 333 pixmap->drawable.pScreen->devPrivate = pixmap; 334} 335 336static 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 347static 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 369static 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 382void 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