1/* 2 * Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas. 3 * Copyright 2011 VMWare, Inc. 4 * All Rights Reserved. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the 8 * "Software"), to deal in the Software without restriction, including 9 * without limitation the rights to use, copy, modify, merge, publish, 10 * distribute, sub license, and/or sell copies of the Software, and to 11 * permit persons to whom the Software is furnished to do so, subject to 12 * the following conditions: 13 * 14 * The above copyright notice and this permission notice (including the 15 * next paragraph) shall be included in all copies or substantial portions 16 * of the Software. 17 * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 21 * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR 22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 * 26 * 27 * Author: Alan Hourihane <alanh@tungstengraphics.com> 28 * Author: Jakob Bornecrantz <wallbraker@gmail.com> 29 * Author: Thomas Hellstrom <thellstrom@vmware.com> 30 * 31 */ 32#ifdef HAVE_CONFIG_H 33#include "config.h" 34#endif 35 36#include "xorg-server.h" 37#include "xf86.h" 38#include "xf86_OSproc.h" 39 40#include "vmwgfx_driver.h" 41#include "../saa/saa.h" 42 43#include "dri2.h" 44#include "gcstruct.h" 45#include "gc.h" 46#include "vmwgfx_saa.h" 47#include "wsbm_util.h" 48#include <unistd.h> 49#include "vmwgfx_hosted.h" 50 51#define VMWGFX_FD_PATH_LEN 80 52 53typedef struct { 54 int refcount; 55 PixmapPtr pPixmap; 56 struct xa_surface *srf; 57 unsigned int dri2_depth; 58} *BufferPrivatePtr; 59 60 61/* 62 * Attempt to guess what the dri state tracker is up to. 63 * Currently it sends only bpp as format. 64 */ 65 66static unsigned int 67vmwgfx_color_format_to_depth(unsigned int format) 68{ 69 return format; 70} 71 72static unsigned int 73vmwgfx_zs_format_to_depth(unsigned int format) 74{ 75 if (format == 24) 76 return 32; 77 return format; 78} 79 80static unsigned int 81vmwgfx_z_format_to_depth(unsigned int format) 82{ 83 return format; 84} 85 86static Bool 87dri2_do_create_buffer(DrawablePtr pDraw, DRI2Buffer2Ptr buffer, unsigned int format) 88{ 89 ScreenPtr pScreen = pDraw->pScreen; 90 ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); 91 modesettingPtr ms = modesettingPTR(pScrn); 92 BufferPrivatePtr private = buffer->driverPrivate; 93 PixmapPtr pPixmap; 94 struct vmwgfx_saa_pixmap *vpix; 95 struct xa_surface *srf = NULL; 96 unsigned int depth; 97 98 99 if (pDraw->type == DRAWABLE_PIXMAP) 100 pPixmap = (PixmapPtr) pDraw; 101 else 102 pPixmap = (*pScreen->GetWindowPixmap)((WindowPtr) pDraw); 103 104 vpix = vmwgfx_saa_pixmap(pPixmap); 105 private->refcount = 0; 106 107 switch (buffer->attachment) { 108 default: 109 depth = (format) ? vmwgfx_color_format_to_depth(format) : 110 pDraw->depth; 111 112 if (buffer->attachment != DRI2BufferFakeFrontLeft || 113 &pPixmap->drawable != pDraw) { 114 115 pPixmap = (*pScreen->CreatePixmap)(pScreen, 116 pDraw->width, 117 pDraw->height, 118 depth, 119 0); 120 if (pPixmap == NullPixmap) 121 return FALSE; 122 123 private->pPixmap = pPixmap; 124 private->dri2_depth = depth; 125 vpix = vmwgfx_saa_pixmap(pPixmap); 126 } 127 break; 128 case DRI2BufferFrontLeft: 129 if (&pPixmap->drawable == pDraw) 130 break; 131 buffer->name = 0; 132 buffer->pitch = 0; 133 buffer->cpp = pDraw->bitsPerPixel / 8; 134 buffer->driverPrivate = private; 135 buffer->flags = 0; /* not tiled */ 136 buffer->format = pDraw->bitsPerPixel; 137 if (!private->pPixmap) { 138 private->dri2_depth = 0; 139 private->pPixmap = pPixmap; 140 pPixmap->refcnt++; 141 } 142 return TRUE; 143 case DRI2BufferStencil: 144 case DRI2BufferDepthStencil: 145 if (!pScrn->vtSema) 146 return FALSE; 147 148 depth = (format) ? vmwgfx_zs_format_to_depth(format) : 32; 149 150 /* 151 * The SVGA device uses the zs ordering. 152 */ 153 154 srf = xa_surface_create(ms->xat, pDraw->width, pDraw->height, 155 depth, xa_type_zs, xa_format_unknown, 156 XA_FLAG_SHARED ); 157 if (!srf) 158 return FALSE; 159 160 private->dri2_depth = depth; 161 162 break; 163 case DRI2BufferDepth: 164 if (!pScrn->vtSema) 165 return FALSE; 166 167 depth = (format) ? vmwgfx_z_format_to_depth(format) : 168 pDraw->bitsPerPixel; 169 170 if (depth == 24) 171 srf = xa_surface_create(ms->xat, pDraw->width, pDraw->height, 172 depth, xa_type_zs, xa_format_unknown, 173 XA_FLAG_SHARED ); 174 else 175 srf = xa_surface_create(ms->xat, pDraw->width, pDraw->height, 176 depth, 177 xa_type_z, xa_format_unknown, 178 XA_FLAG_SHARED); 179 if (!srf) 180 return FALSE; 181 182 private->dri2_depth = depth; 183 184 break; 185 } 186 187 if (!private->pPixmap) { 188 private->pPixmap = pPixmap; 189 pPixmap->refcnt++; 190 } 191 192 if (!srf) { 193 depth = (format) ? vmwgfx_color_format_to_depth(format) : 194 pDraw->depth; 195 196 if (!vmwgfx_hw_dri2_validate(pPixmap, depth)) 197 return FALSE; 198 199 srf = vpix->hw; 200 private->refcount++; 201 private->dri2_depth = depth; 202 203 /* 204 * Compiz workaround. See vmwgfx_dirty(); 205 */ 206 207 if (buffer->attachment == DRI2BufferFrontLeft || 208 buffer->attachment == DRI2BufferFakeFrontLeft) 209 vpix->hw_is_dri2_fronts++; 210 } 211 212 private->srf = srf; 213 if (_xa_surface_handle(srf, &buffer->name, &buffer->pitch) != 0) 214 return FALSE; 215 216 buffer->cpp = xa_format_depth(xa_surface_format(srf)) / 8; 217 buffer->driverPrivate = private; 218 buffer->flags = 0; /* not tiled */ 219 buffer->format = format; 220 private->refcount++; 221 222 return TRUE; 223} 224 225static void 226dri2_do_destroy_buffer(DrawablePtr pDraw, DRI2BufferPtr buffer) 227{ 228 BufferPrivatePtr private = buffer->driverPrivate; 229 struct xa_surface *srf = private->srf; 230 ScreenPtr pScreen = pDraw->pScreen; 231 struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(private->pPixmap); 232 233 if (--private->refcount == 0 && srf) { 234 xa_surface_destroy(srf); 235 } 236 237 /* 238 * Compiz workaround. See vmwgfx_dirty(); 239 */ 240 241 if ((buffer->attachment == DRI2BufferFrontLeft || 242 buffer->attachment == DRI2BufferFakeFrontLeft) && 243 private->refcount == 1 && 244 --vpix->hw_is_dri2_fronts == 0) 245 WSBMLISTDELINIT(&vpix->sync_x_head); 246 247 private->srf = NULL; 248 pScreen->DestroyPixmap(private->pPixmap); 249} 250 251 252static DRI2Buffer2Ptr 253dri2_create_buffer(DrawablePtr pDraw, unsigned int attachment, unsigned int format) 254{ 255 DRI2Buffer2Ptr buffer; 256 BufferPrivatePtr private; 257 258 buffer = calloc(1, sizeof *buffer); 259 if (!buffer) 260 return NULL; 261 262 private = calloc(1, sizeof *private); 263 if (!private) { 264 goto fail; 265 } 266 267 buffer->attachment = attachment; 268 buffer->driverPrivate = private; 269 270 if (dri2_do_create_buffer(pDraw, buffer, format)) 271 return buffer; 272 273 free(private); 274fail: 275 free(buffer); 276 return NULL; 277} 278 279static void 280dri2_destroy_buffer(DrawablePtr pDraw, DRI2Buffer2Ptr buffer) 281{ 282 /* So far it is safe to downcast a DRI2Buffer2Ptr to DRI2BufferPtr */ 283 dri2_do_destroy_buffer(pDraw, (DRI2BufferPtr)buffer); 284 285 free(buffer->driverPrivate); 286 free(buffer); 287} 288 289static void 290dri2_copy_region(DrawablePtr pDraw, RegionPtr pRegion, 291 DRI2Buffer2Ptr pDestBuffer, DRI2Buffer2Ptr pSrcBuffer) 292{ 293 294 295 ScreenPtr pScreen = pDraw->pScreen; 296 BufferPrivatePtr dst_priv = pDestBuffer->driverPrivate; 297 BufferPrivatePtr src_priv = pSrcBuffer->driverPrivate; 298 DrawablePtr src_draw; 299 DrawablePtr dst_draw; 300 RegionPtr myClip; 301 GCPtr gc; 302 ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); 303 304 /* 305 * This is a fragile protection against HW operations when not master. 306 * Needs to be blocked higher up in the dri2 code. 307 */ 308 if (!pScrn->vtSema) 309 return; 310 311 /* 312 * In driCreateBuffers we dewrap windows into the 313 * backing pixmaps in order to get to the texture. 314 * We need to use the real drawable in CopyArea 315 * so that cliprects and offsets are correct. 316 */ 317 src_draw = (pSrcBuffer->attachment == DRI2BufferFrontLeft) ? pDraw : 318 &src_priv->pPixmap->drawable; 319 dst_draw = (pDestBuffer->attachment == DRI2BufferFrontLeft) ? pDraw : 320 &dst_priv->pPixmap->drawable; 321 322 /* 323 * The clients implements glXWaitX with a copy front to fake and then 324 * waiting on the server to signal its completion of it. While 325 * glXWaitGL is a client side flush and a copy from fake to front. 326 * This is how it is done in the DRI2 protocol, how ever depending 327 * which type of drawables the server does things a bit differently 328 * then what the protocol says as the fake and front are the same. 329 * 330 * for pixmaps glXWaitX is a server flush. 331 * for pixmaps glXWaitGL is a client flush. 332 * for windows glXWaitX is a copy from front to fake then a server flush. 333 * for windows glXWaitGL is a client flush then a copy from fake to front. 334 * 335 * XXX in the windows case this code always flushes but that isn't a 336 * must in the glXWaitGL case but we don't know if this is a glXWaitGL 337 * or a glFlush/glFinish call. 338 */ 339 if (dst_priv->pPixmap == src_priv->pPixmap) { 340 /* pixmap glXWaitX */ 341 if (pSrcBuffer->attachment == DRI2BufferFrontLeft && 342 pDestBuffer->attachment == DRI2BufferFakeFrontLeft) { 343 344 if (!vmwgfx_hw_dri2_validate(dst_priv->pPixmap, 345 dst_priv->dri2_depth)) 346 return; 347 } 348 /* pixmap glXWaitGL */ 349 if (pDestBuffer->attachment == DRI2BufferFrontLeft && 350 pSrcBuffer->attachment == DRI2BufferFakeFrontLeft) { 351 return; 352 } else { 353 vmwgfx_flush_dri2(pScreen); 354 return; 355 } 356 } 357 358 gc = GetScratchGC(pDraw->depth, pScreen); 359 myClip = REGION_CREATE(pScreen, REGION_RECTS(pRegion), 360 REGION_NUM_RECTS(pRegion)); 361 (*gc->funcs->ChangeClip) (gc, CT_REGION, myClip, 0); 362 ValidateGC(dst_draw, gc); 363 364 /* 365 * Damage the src drawable in order for damageCopyArea to pick up 366 * that something changed. 367 */ 368 DamageRegionAppend(src_draw, pRegion); 369 if (pSrcBuffer->attachment != DRI2BufferFrontLeft) 370 saa_drawable_dirty(src_draw, TRUE, pRegion); 371 DamageRegionProcessPending(src_draw); 372 373 /* 374 * Call CopyArea. This usually means a call to damageCopyArea that 375 * is wrapping saa_copy_area. The damageCopyArea function will make 376 * sure the destination drawable is appropriately damaged. 377 */ 378 (*gc->ops->CopyArea)(src_draw, dst_draw, gc, 379 0, 0, pDraw->width, pDraw->height, 0, 0); 380 381 /* 382 * FreeScratchGC will free myClip as well. 383 */ 384 myClip = NULL; 385 FreeScratchGC(gc); 386} 387 388#if (DRI2INFOREC_VERSION >= 8 && DRI2INFOREC_VERSION < 10) 389static int vmw_dri_auth_magic2(ScreenPtr pScreen, uint32_t magic) 390{ 391 ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); 392 modesettingPtr ms = modesettingPTR(pScrn); 393 394 return vmwgfx_hosted_dri_auth(ms->hdriver, ms->hosted, NULL, magic); 395} 396#endif 397 398#if (DRI2INFOREC_VERSION >= 10) 399static int vmw_dri_auth_magic3(ClientPtr client, ScreenPtr pScreen, 400 uint32_t magic) 401{ 402 ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); 403 modesettingPtr ms = modesettingPTR(pScrn); 404 405 return vmwgfx_hosted_dri_auth(ms->hdriver, ms->hosted, client, magic); 406} 407#endif 408 409Bool 410xorg_dri2_init(ScreenPtr pScreen) 411{ 412 ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); 413 modesettingPtr ms = modesettingPTR(pScrn); 414 DRI2InfoRec dri2info; 415 int major, minor; 416 417 memset(&dri2info, 0, sizeof(dri2info)); 418 419 if (xf86LoaderCheckSymbol("DRI2Version")) { 420 DRI2Version(&major, &minor); 421 } else { 422 /* Assume version 1.0 */ 423 major = 1; 424 minor = 0; 425 } 426 427 dri2info.version = min(DRI2INFOREC_VERSION, 3); 428 dri2info.fd = ms->fd; 429 dri2info.driverName = "vmwgfx"; 430 431#ifdef VMWGFX_LIBDRM_DEVICENAME 432 ms->dri2_device_name = drmGetDeviceNameFromFd2(ms->fd); 433 434 if (!ms->dri2_device_name) { 435 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, 436 "Could not find the drm device name. Disabling dri2.\n"); 437 return FALSE; 438 } 439#else 440 /* 441 * This way of obtaining the DRM device name is a bit 442 * os-specific. Currently this works only for Linux. 443 */ 444 { 445 char fdPath[VMWGFX_FD_PATH_LEN]; 446 ssize_t numChar; 447 448 memset(fdPath, 0, VMWGFX_FD_PATH_LEN); 449 snprintf(fdPath, VMWGFX_FD_PATH_LEN - 1, "/proc/self/fd/%d", ms->fd); 450 numChar = readlink(fdPath, ms->dri2_device_name, VMWGFX_DRI_DEVICE_LEN); 451 if (numChar <= 0 || numChar >= VMWGFX_DRI_DEVICE_LEN) { 452 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, 453 "Could not find the drm device name. Disabling dri2.\n"); 454 return FALSE; 455 } 456 ms->dri2_device_name[numChar] = 0; 457 } 458#endif 459 dri2info.deviceName = ms->dri2_device_name; 460 xf86DrvMsg(pScrn->scrnIndex, X_INFO, 461 "Path of drm device is \"%s\".\n", ms->dri2_device_name); 462 463 dri2info.CreateBuffer = dri2_create_buffer; 464 dri2info.DestroyBuffer = dri2_destroy_buffer; 465 466 dri2info.CopyRegion = dri2_copy_region; 467 dri2info.Wait = NULL; 468 469#if (DRI2INFOREC_VERSION >= 8 && DRI2INFOREC_VERSION < 10) 470 if (vmwgfx_is_hosted(ms->hdriver)) { 471 dri2info.version = 8; 472 dri2info.AuthMagic2 = vmw_dri_auth_magic2; 473 } 474#endif 475#if (DRI2INFOREC_VERSION >= 10) 476 if (vmwgfx_is_hosted(ms->hdriver)) { 477 dri2info.version = 10; 478 dri2info.AuthMagic3 = vmw_dri_auth_magic3; 479 } 480#endif 481 482 return DRI2ScreenInit(pScreen, &dri2info); 483} 484 485void 486xorg_dri2_close(ScreenPtr pScreen) 487{ 488#ifdef VMWGFX_LIBDRM_DEVICENAME 489 ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); 490 modesettingPtr ms = modesettingPTR(pScrn); 491 492 free(ms->dri2_device_name); 493#endif 494 495 DRI2CloseScreen(pScreen); 496} 497 498/* vim: set sw=4 ts=8 sts=4: */ 499