Home | History | Annotate | Line # | Download | only in src
      1 /*
      2  * Copyright 2008 Red Hat, 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  * on the rights to use, copy, modify, merge, publish, distribute, sub
      8  * license, and/or sell copies of the Software, and to permit persons to whom
      9  * the 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 NON-INFRINGEMENT.  IN NO EVENT SHALL
     18  * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
     19  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     20  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     21  */
     22 
     23 /** \file qxl_driver.c
     24  * \author Adam Jackson <ajax (at) redhat.com>
     25  * \author Sren Sandmann <sandmann (at) redhat.com>
     26  *
     27  * This is qxl, a driver for the Qumranet paravirtualized graphics device
     28  * in qemu.
     29  */
     30 
     31 
     32 #ifdef HAVE_CONFIG_H
     33 #include "config.h"
     34 #endif
     35 
     36 #include "qxl.h"
     37 #include "dfps.h"
     38 #include <spice/protocol.h>
     39 
     40 #if HAS_DEVPRIVATEKEYREC
     41 DevPrivateKeyRec uxa_pixmap_index;
     42 #else
     43 int uxa_pixmap_index;
     44 #endif
     45 
     46 static Bool
     47 qxl_prepare_access (PixmapPtr pixmap, RegionPtr region, uxa_access_t access)
     48 {
     49     return qxl_surface_prepare_access (get_surface (pixmap),
     50                                        pixmap, region, access);
     51 }
     52 
     53 static void
     54 qxl_finish_access (PixmapPtr pixmap)
     55 {
     56     qxl_surface_finish_access (get_surface (pixmap), pixmap);
     57 }
     58 
     59 static Bool
     60 qxl_pixmap_is_offscreen (PixmapPtr pixmap)
     61 {
     62     return !!get_surface (pixmap);
     63 }
     64 
     65 static Bool
     66 good_alu_and_pm (DrawablePtr drawable, int alu, Pixel planemask)
     67 {
     68     if (!UXA_PM_IS_SOLID (drawable, planemask))
     69 	return FALSE;
     70 
     71     if (alu != GXcopy)
     72 	return FALSE;
     73 
     74     return TRUE;
     75 }
     76 
     77 /*
     78  * Solid fill
     79  */
     80 static Bool
     81 qxl_check_solid (DrawablePtr drawable, int alu, Pixel planemask)
     82 {
     83     if (!good_alu_and_pm (drawable, alu, planemask))
     84 	return FALSE;
     85 
     86     return TRUE;
     87 }
     88 
     89 static Bool
     90 qxl_prepare_solid (PixmapPtr pixmap, int alu, Pixel planemask, Pixel fg)
     91 {
     92     qxl_surface_t *surface;
     93 
     94     if (!(surface = get_surface (pixmap)))
     95 	return FALSE;
     96 
     97     return qxl_surface_prepare_solid (surface, fg);
     98 }
     99 
    100 static void
    101 qxl_solid (PixmapPtr pixmap, int x1, int y1, int x2, int y2)
    102 {
    103     qxl_surface_solid (get_surface (pixmap), x1, y1, x2, y2);
    104 }
    105 
    106 static void
    107 qxl_done_solid (PixmapPtr pixmap)
    108 {
    109 }
    110 
    111 /*
    112  * Copy
    113  */
    114 static Bool
    115 qxl_check_copy (PixmapPtr source, PixmapPtr dest,
    116                 int alu, Pixel planemask)
    117 {
    118     if (!good_alu_and_pm ((DrawablePtr)source, alu, planemask))
    119 	return FALSE;
    120 
    121     if (source->drawable.bitsPerPixel != dest->drawable.bitsPerPixel)
    122     {
    123 	ErrorF ("differing bitsperpixel - this shouldn't happen\n");
    124 	return FALSE;
    125     }
    126 
    127     return TRUE;
    128 }
    129 
    130 static Bool
    131 qxl_prepare_copy (PixmapPtr source, PixmapPtr dest,
    132                   int xdir, int ydir, int alu,
    133                   Pixel planemask)
    134 {
    135     return qxl_surface_prepare_copy (get_surface (dest), get_surface (source));
    136 }
    137 
    138 static void
    139 qxl_copy (PixmapPtr dest,
    140           int src_x1, int src_y1,
    141           int dest_x1, int dest_y1,
    142           int width, int height)
    143 {
    144     qxl_surface_copy (get_surface (dest),
    145                       src_x1, src_y1,
    146                       dest_x1, dest_y1,
    147                       width, height);
    148 }
    149 
    150 static void
    151 qxl_done_copy (PixmapPtr dest)
    152 {
    153 }
    154 
    155 /*
    156  * Composite
    157  */
    158 static Bool
    159 can_accelerate_picture (qxl_screen_t *qxl, PicturePtr pict)
    160 {
    161     if (!pict)
    162 	return TRUE;
    163 
    164     if (pict->format != PICT_a8r8g8b8		&&
    165 	pict->format != PICT_x8r8g8b8		&&
    166 	pict->format != PICT_a8)
    167     {
    168         if (qxl->debug_render_fallbacks)
    169         {
    170             ErrorF ("Image with format %x can't be accelerated \n",
    171                     pict->format);
    172         }
    173 
    174         return FALSE;
    175     }
    176 
    177     if (!pict->pDrawable)
    178     {
    179         if (qxl->debug_render_fallbacks)
    180         {
    181             ErrorF ("Source image (of type %d) can't be accelerated\n",
    182                     pict->pSourcePict->type);
    183         }
    184 
    185 	return FALSE;
    186     }
    187 
    188     if (pict->transform)
    189     {
    190 	if (pict->transform->matrix[2][0] != 0	||
    191 	    pict->transform->matrix[2][1] != 0	||
    192 	    pict->transform->matrix[2][2] != pixman_int_to_fixed (1))
    193 	{
    194             if (qxl->debug_render_fallbacks)
    195                 ErrorF ("Image with non-affine transform can't be accelerated\n");
    196 
    197 	    return FALSE;
    198 	}
    199     }
    200 
    201     if (pict->filter != PictFilterBilinear	&&
    202 	pict->filter != PictFilterNearest)
    203     {
    204         if (qxl->debug_render_fallbacks)
    205         {
    206             ErrorF ("Image with filter type %d can't be accelerated\n",
    207                     pict->filter);
    208         }
    209 
    210         return FALSE;
    211     }
    212 
    213     return TRUE;
    214 }
    215 
    216 #define QXL_HAS_CAP(qxl, cap)						\
    217     (((qxl)->rom->client_capabilities[(cap) / 8]) & (1 << ((cap) % 8)))
    218 
    219 static Bool
    220 qxl_has_composite (qxl_screen_t *qxl)
    221 {
    222 #ifdef XF86DRM_MODE
    223     if (qxl->kms_enabled) {
    224 #if 0 /* KMS Composite support seems broken - needs better hw support */
    225 	static Bool result, checked;
    226 	if (!checked) {
    227 	    result = qxl_kms_check_cap(qxl, SPICE_DISPLAY_CAP_COMPOSITE);
    228 	    checked = TRUE;
    229 	}
    230 	return result;
    231 #else
    232 	return FALSE;
    233 #endif
    234     }
    235 #endif
    236 #ifndef XSPICE
    237     return
    238 	qxl->pci->revision >= 4			&&
    239 	QXL_HAS_CAP (qxl, SPICE_DISPLAY_CAP_COMPOSITE);
    240 #else
    241     /* FIXME */
    242     return FALSE;
    243 #endif
    244 }
    245 
    246 static Bool
    247 qxl_has_a8_surfaces (qxl_screen_t *qxl)
    248 {
    249 #ifdef XF86DRM_MODE
    250     if (qxl->kms_enabled) {
    251 #if 0 /* KMS Composite support seems broken - needs better hw support */
    252         static Bool result, checked;
    253 	if (!checked) {
    254             result = qxl_kms_check_cap(qxl, SPICE_DISPLAY_CAP_A8_SURFACE);
    255 	    checked = TRUE;
    256 	}
    257 	return result;
    258 #else
    259 	return FALSE;
    260 #endif
    261     }
    262 #endif
    263 #ifndef XSPICE
    264     if (qxl->pci->revision < 4)
    265     {
    266         if (qxl->debug_render_fallbacks)
    267         {
    268             ErrorF ("No a8 surface due to revision being %d, which is < 4\n",
    269                     qxl->pci->revision);
    270         }
    271 
    272         return FALSE;
    273     }
    274 
    275     if (!QXL_HAS_CAP (qxl, SPICE_DISPLAY_CAP_COMPOSITE))
    276     {
    277         if (qxl->debug_render_fallbacks)
    278         {
    279             ErrorF ("No composite due to client not providing SPICE_DISPLAY_CAP_A8_SURFACE\n");
    280         }
    281 
    282         return FALSE;
    283     }
    284 
    285     return TRUE;
    286 
    287 #else
    288     /* FIXME */
    289     return FALSE;
    290 #endif
    291 }
    292 
    293 static Bool
    294 qxl_check_composite (int op,
    295 		     PicturePtr pSrcPicture,
    296 		     PicturePtr pMaskPicture,
    297 		     PicturePtr pDstPicture,
    298 		     int width, int height)
    299 {
    300     int i;
    301     ScreenPtr pScreen = pDstPicture->pDrawable->pScreen;
    302     ScrnInfoPtr pScrn = xf86ScreenToScrn (pScreen);
    303     qxl_screen_t *qxl = pScrn->driverPrivate;
    304 
    305     static const int accelerated_ops[] =
    306     {
    307 	PictOpClear, PictOpSrc, PictOpDst, PictOpOver, PictOpOverReverse,
    308 	PictOpIn, PictOpInReverse, PictOpOut, PictOpOutReverse,
    309 	PictOpAtop, PictOpAtopReverse, PictOpXor, PictOpAdd,
    310 	PictOpSaturate, PictOpMultiply, PictOpScreen, PictOpOverlay,
    311 	PictOpDarken, PictOpLighten, PictOpColorDodge, PictOpColorBurn,
    312 	PictOpHardLight, PictOpSoftLight, PictOpDifference, PictOpExclusion,
    313 	PictOpHSLHue, PictOpHSLSaturation, PictOpHSLColor, PictOpHSLLuminosity,
    314     };
    315 
    316     if (!qxl_has_composite (qxl))
    317 	return FALSE;
    318 
    319     if (!can_accelerate_picture (qxl, pSrcPicture)	||
    320 	!can_accelerate_picture (qxl, pMaskPicture)	||
    321 	!can_accelerate_picture (qxl, pDstPicture))
    322     {
    323 	return FALSE;
    324     }
    325 
    326     for (i = 0; i < sizeof (accelerated_ops) / sizeof (accelerated_ops[0]); ++i)
    327     {
    328 	if (accelerated_ops[i] == op)
    329 	    goto found;
    330     }
    331 
    332     if (qxl->debug_render_fallbacks)
    333         ErrorF ("Compositing operator %d can't be accelerated\n", op);
    334 
    335     return FALSE;
    336 
    337 found:
    338     return TRUE;
    339 }
    340 
    341 static Bool
    342 qxl_check_composite_target (PixmapPtr pixmap)
    343 {
    344     return TRUE;
    345 }
    346 
    347 static Bool
    348 qxl_check_composite_texture (ScreenPtr screen,
    349 			     PicturePtr pPicture)
    350 {
    351     return TRUE;
    352 }
    353 
    354 static Bool
    355 qxl_prepare_composite (int op,
    356 		       PicturePtr pSrcPicture,
    357 		       PicturePtr pMaskPicture,
    358 		       PicturePtr pDstPicture,
    359 		       PixmapPtr pSrc,
    360 		       PixmapPtr pMask,
    361 		       PixmapPtr pDst)
    362 {
    363     return qxl_surface_prepare_composite (
    364 	op, pSrcPicture, pMaskPicture, pDstPicture,
    365 	get_surface (pSrc),
    366 	pMask? get_surface (pMask) : NULL,
    367 	get_surface (pDst));
    368 }
    369 
    370 static void
    371 qxl_composite (PixmapPtr pDst,
    372 	       int src_x, int src_y,
    373 	       int mask_x, int mask_y,
    374 	       int dst_x, int dst_y,
    375 	       int width, int height)
    376 {
    377     qxl_surface_composite (
    378 	get_surface (pDst),
    379 	src_x, src_y,
    380 	mask_x, mask_y,
    381 	dst_x, dst_y, width, height);
    382 }
    383 
    384 static void
    385 qxl_done_composite (PixmapPtr pDst)
    386 {
    387     ;
    388 }
    389 
    390 static Bool
    391 qxl_put_image (PixmapPtr pDst, int x, int y, int w, int h,
    392                char *src, int src_pitch)
    393 {
    394     qxl_surface_t *surface = get_surface (pDst);
    395 
    396     if (surface)
    397 	return qxl_surface_put_image (surface, x, y, w, h, src, src_pitch);
    398 
    399     return FALSE;
    400 }
    401 
    402 static void
    403 qxl_set_screen_pixmap (PixmapPtr pixmap)
    404 {
    405     pixmap->drawable.pScreen->devPrivate = pixmap;
    406 }
    407 
    408 static PixmapPtr
    409 qxl_create_pixmap (ScreenPtr screen, int w, int h, int depth, unsigned usage)
    410 {
    411     ScrnInfoPtr    scrn = xf86ScreenToScrn (screen);
    412     PixmapPtr      pixmap;
    413     qxl_screen_t * qxl = scrn->driverPrivate;
    414     qxl_surface_t *surface;
    415 
    416     if (w > 32767 || h > 32767)
    417 	return NULL;
    418 
    419     qxl_surface_cache_sanity_check (qxl->surface_cache);
    420 
    421 #if 0
    422     ErrorF ("Create pixmap: %d %d @ %d (usage: %d)\n", w, h, depth, usage);
    423 #endif
    424 
    425     if (qxl->kms_enabled)
    426 	goto fallback;
    427     if (uxa_swapped_out (screen))
    428 	goto fallback;
    429 
    430     if (depth == 8 && !qxl_has_a8_surfaces (qxl))
    431     {
    432 	/* FIXME: When we detect a _change_ in the property of having a8
    433 	 * surfaces, we should copy all existing a8 surface to host memory
    434 	 * and then destroy the ones on the device.
    435 	 */
    436 	goto fallback;
    437     }
    438 
    439     if (!w || !h)
    440       goto fallback;
    441 
    442     surface = qxl->bo_funcs->create_surface (qxl, w, h, depth);
    443     if (surface)
    444     {
    445 	/* ErrorF ("   Successfully created surface in video memory\n"); */
    446 
    447 	pixmap = fbCreatePixmap (screen, 0, 0, depth, usage);
    448 
    449 	screen->ModifyPixmapHeader (pixmap, w, h,
    450 	                            -1, -1, -1,
    451 	                            NULL);
    452 
    453 #if 0
    454 	ErrorF ("Create pixmap %p with surface %p\n", pixmap, surface);
    455 #endif
    456 	set_surface (pixmap, surface);
    457 	qxl_surface_set_pixmap (surface, pixmap);
    458 
    459 	qxl_surface_cache_sanity_check (qxl->surface_cache);
    460     }
    461     else
    462     {
    463 #if 0
    464 	ErrorF ("   Couldn't allocate %d x %d @ %d surface in video memory\n",
    465 	        w, h, depth);
    466 #endif
    467     fallback:
    468 	pixmap = fbCreatePixmap (screen, w, h, depth, usage);
    469 
    470 #if 0
    471 	ErrorF ("Create pixmap %p without surface\n", pixmap);
    472 #endif
    473     }
    474 
    475     return pixmap;
    476 }
    477 
    478 static Bool
    479 qxl_destroy_pixmap (PixmapPtr pixmap)
    480 {
    481     ScreenPtr      screen = pixmap->drawable.pScreen;
    482     ScrnInfoPtr    scrn = xf86ScreenToScrn (screen);
    483     qxl_screen_t * qxl = scrn->driverPrivate;
    484     qxl_surface_t *surface = NULL;
    485 
    486     qxl_surface_cache_sanity_check (qxl->surface_cache);
    487 
    488     if (pixmap->refcnt == 1)
    489     {
    490 	surface = get_surface (pixmap);
    491 
    492 #if 0
    493 	ErrorF ("- Destroy %p (had surface %p)\n", pixmap, surface);
    494 #endif
    495 
    496 	if (surface)
    497 	{
    498 	    qxl->bo_funcs->destroy_surface(surface);
    499 	    set_surface (pixmap, NULL);
    500 
    501 	    qxl_surface_cache_sanity_check (qxl->surface_cache);
    502 	}
    503     }
    504 
    505     fbDestroyPixmap (pixmap);
    506     return TRUE;
    507 }
    508 
    509 static void
    510 set_uxa_functions(qxl_screen_t *qxl, ScreenPtr screen)
    511 {
    512     /* Solid fill */
    513     qxl->uxa->check_solid = qxl_check_solid;
    514     qxl->uxa->prepare_solid = qxl_prepare_solid;
    515     qxl->uxa->solid = qxl_solid;
    516     qxl->uxa->done_solid = qxl_done_solid;
    517 
    518     /* Copy */
    519     qxl->uxa->check_copy = qxl_check_copy;
    520     qxl->uxa->prepare_copy = qxl_prepare_copy;
    521     qxl->uxa->copy = qxl_copy;
    522     qxl->uxa->done_copy = qxl_done_copy;
    523 
    524     /* Composite */
    525     qxl->uxa->check_composite = qxl_check_composite;
    526     qxl->uxa->check_composite_target = qxl_check_composite_target;
    527     qxl->uxa->check_composite_texture = qxl_check_composite_texture;
    528     qxl->uxa->prepare_composite = qxl_prepare_composite;
    529     qxl->uxa->composite = qxl_composite;
    530     qxl->uxa->done_composite = qxl_done_composite;
    531 
    532     /* PutImage */
    533     qxl->uxa->put_image = qxl_put_image;
    534 
    535     /* Prepare access */
    536     qxl->uxa->prepare_access = qxl_prepare_access;
    537     qxl->uxa->finish_access = qxl_finish_access;
    538 
    539     qxl->uxa->pixmap_is_offscreen = qxl_pixmap_is_offscreen;
    540 
    541     screen->SetScreenPixmap = qxl_set_screen_pixmap;
    542     screen->CreatePixmap = qxl_create_pixmap;
    543     screen->DestroyPixmap = qxl_destroy_pixmap;
    544 }
    545 
    546 Bool
    547 qxl_uxa_init (qxl_screen_t *qxl, ScreenPtr screen)
    548 {
    549     ScrnInfoPtr scrn = xf86ScreenToScrn (screen);
    550 
    551 #if HAS_DIXREGISTERPRIVATEKEY
    552     if (!dixRegisterPrivateKey (&uxa_pixmap_index, PRIVATE_PIXMAP, 0))
    553 	return FALSE;
    554 #else
    555     if (!dixRequestPrivate (&uxa_pixmap_index, 0))
    556 	return FALSE;
    557 #endif
    558 
    559     qxl->uxa = uxa_driver_alloc ();
    560     if (qxl->uxa == NULL)
    561 	return FALSE;
    562 
    563     memset (qxl->uxa, 0, sizeof (*qxl->uxa));
    564 
    565     qxl->uxa->uxa_major = 1;
    566     qxl->uxa->uxa_minor = 0;
    567 
    568     if (qxl->deferred_fps)
    569         dfps_set_uxa_functions(qxl, screen);
    570     else
    571         set_uxa_functions(qxl, screen);
    572 
    573     if (!uxa_driver_init (screen, qxl->uxa))
    574     {
    575 	xf86DrvMsg (scrn->scrnIndex, X_ERROR,
    576 	            "UXA initialization failed\n");
    577 	free (qxl->uxa);
    578 	return FALSE;
    579     }
    580 
    581 #if 0
    582     uxa_set_fallback_debug (screen, FALSE);
    583 #endif
    584 
    585 #if 0
    586     if (!uxa_driver_init (screen, qxl->uxa))
    587 	return FALSE;
    588 #endif
    589 
    590     return TRUE;
    591 }
    592