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@redhat.com> 25 * \author Søren Sandmann <sandmann@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 41DevPrivateKeyRec uxa_pixmap_index; 42#else 43int uxa_pixmap_index; 44#endif 45 46static Bool 47qxl_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 53static void 54qxl_finish_access (PixmapPtr pixmap) 55{ 56 qxl_surface_finish_access (get_surface (pixmap), pixmap); 57} 58 59static Bool 60qxl_pixmap_is_offscreen (PixmapPtr pixmap) 61{ 62 return !!get_surface (pixmap); 63} 64 65static Bool 66good_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 */ 80static Bool 81qxl_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 89static Bool 90qxl_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 100static void 101qxl_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 106static void 107qxl_done_solid (PixmapPtr pixmap) 108{ 109} 110 111/* 112 * Copy 113 */ 114static Bool 115qxl_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 130static Bool 131qxl_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 138static void 139qxl_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 150static void 151qxl_done_copy (PixmapPtr dest) 152{ 153} 154 155/* 156 * Composite 157 */ 158static Bool 159can_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 219static Bool 220qxl_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 246static Bool 247qxl_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 293static Bool 294qxl_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 337found: 338 return TRUE; 339} 340 341static Bool 342qxl_check_composite_target (PixmapPtr pixmap) 343{ 344 return TRUE; 345} 346 347static Bool 348qxl_check_composite_texture (ScreenPtr screen, 349 PicturePtr pPicture) 350{ 351 return TRUE; 352} 353 354static Bool 355qxl_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 370static void 371qxl_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 384static void 385qxl_done_composite (PixmapPtr pDst) 386{ 387 ; 388} 389 390static Bool 391qxl_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 402static void 403qxl_set_screen_pixmap (PixmapPtr pixmap) 404{ 405 pixmap->drawable.pScreen->devPrivate = pixmap; 406} 407 408static PixmapPtr 409qxl_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 478static Bool 479qxl_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 509static void 510set_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 546Bool 547qxl_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