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