dri2.c revision 52397711
1/* 2 * Copyright © 2007, 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 "Soft- 6 * ware"), to deal in the Software without restriction, including without 7 * limitation the rights to use, copy, modify, merge, publish, distribute, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, provided that the above copyright 10 * notice(s) and this permission notice appear in all copies of the Soft- 11 * ware and that both the above copyright notice(s) and this permission 12 * notice appear in supporting documentation. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- 16 * ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY 17 * RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN 18 * THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSE- 19 * QUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 20 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 21 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFOR- 22 * MANCE OF THIS SOFTWARE. 23 * 24 * Except as contained in this notice, the name of a copyright holder shall 25 * not be used in advertising or otherwise to promote the sale, use or 26 * other dealings in this Software without prior written authorization of 27 * the copyright holder. 28 * 29 * Authors: 30 * Kristian Høgsberg (krh@redhat.com) 31 */ 32 33#ifdef HAVE_XORG_CONFIG_H 34#include <xorg-config.h> 35#endif 36 37#include <xf86drm.h> 38#include "xf86Module.h" 39#include "scrnintstr.h" 40#include "windowstr.h" 41#include "dri2.h" 42 43#include "xf86.h" 44 45static int dri2ScreenPrivateKeyIndex; 46static DevPrivateKey dri2ScreenPrivateKey = &dri2ScreenPrivateKeyIndex; 47static int dri2WindowPrivateKeyIndex; 48static DevPrivateKey dri2WindowPrivateKey = &dri2WindowPrivateKeyIndex; 49static int dri2PixmapPrivateKeyIndex; 50static DevPrivateKey dri2PixmapPrivateKey = &dri2PixmapPrivateKeyIndex; 51 52typedef struct _DRI2Drawable { 53 unsigned int refCount; 54 int width; 55 int height; 56 DRI2Buffer2Ptr *buffers; 57 int bufferCount; 58 unsigned int pendingSequence; 59} DRI2DrawableRec, *DRI2DrawablePtr; 60 61typedef struct _DRI2Screen { 62 const char *driverName; 63 const char *deviceName; 64 int fd; 65 unsigned int lastSequence; 66 67 DRI2CreateBuffersProcPtr CreateBuffers; 68 DRI2DestroyBuffersProcPtr DestroyBuffers; 69 70 DRI2CreateBufferProcPtr CreateBuffer; 71 DRI2DestroyBufferProcPtr DestroyBuffer; 72 DRI2CopyRegionProcPtr CopyRegion; 73 74 HandleExposuresProcPtr HandleExposures; 75} DRI2ScreenRec, *DRI2ScreenPtr; 76 77static DRI2ScreenPtr 78DRI2GetScreen(ScreenPtr pScreen) 79{ 80 return dixLookupPrivate(&pScreen->devPrivates, dri2ScreenPrivateKey); 81} 82 83static DRI2DrawablePtr 84DRI2GetDrawable(DrawablePtr pDraw) 85{ 86 WindowPtr pWin; 87 PixmapPtr pPixmap; 88 89 if (pDraw->type == DRAWABLE_WINDOW) 90 { 91 pWin = (WindowPtr) pDraw; 92 return dixLookupPrivate(&pWin->devPrivates, dri2WindowPrivateKey); 93 } 94 else 95 { 96 pPixmap = (PixmapPtr) pDraw; 97 return dixLookupPrivate(&pPixmap->devPrivates, dri2PixmapPrivateKey); 98 } 99} 100 101int 102DRI2CreateDrawable(DrawablePtr pDraw) 103{ 104 WindowPtr pWin; 105 PixmapPtr pPixmap; 106 DRI2DrawablePtr pPriv; 107 108 pPriv = DRI2GetDrawable(pDraw); 109 if (pPriv != NULL) 110 { 111 pPriv->refCount++; 112 return Success; 113 } 114 115 pPriv = xalloc(sizeof *pPriv); 116 if (pPriv == NULL) 117 return BadAlloc; 118 119 pPriv->refCount = 1; 120 pPriv->width = pDraw->width; 121 pPriv->height = pDraw->height; 122 pPriv->buffers = NULL; 123 pPriv->bufferCount = 0; 124 125 if (pDraw->type == DRAWABLE_WINDOW) 126 { 127 pWin = (WindowPtr) pDraw; 128 dixSetPrivate(&pWin->devPrivates, dri2WindowPrivateKey, pPriv); 129 } 130 else 131 { 132 pPixmap = (PixmapPtr) pDraw; 133 dixSetPrivate(&pPixmap->devPrivates, dri2PixmapPrivateKey, pPriv); 134 } 135 136 return Success; 137} 138 139static int 140find_attachment(DRI2DrawablePtr pPriv, unsigned attachment) 141{ 142 int i; 143 144 if (pPriv->buffers == NULL) { 145 return -1; 146 } 147 148 for (i = 0; i < pPriv->bufferCount; i++) { 149 if ((pPriv->buffers[i] != NULL) 150 && (pPriv->buffers[i]->attachment == attachment)) { 151 return i; 152 } 153 } 154 155 return -1; 156} 157 158static DRI2Buffer2Ptr 159allocate_or_reuse_buffer(DrawablePtr pDraw, DRI2ScreenPtr ds, 160 DRI2DrawablePtr pPriv, 161 unsigned int attachment, unsigned int format, 162 int dimensions_match) 163{ 164 DRI2Buffer2Ptr buffer; 165 int old_buf; 166 167 old_buf = find_attachment(pPriv, attachment); 168 169 if ((old_buf < 0) 170 || !dimensions_match 171 || (pPriv->buffers[old_buf]->format != format)) { 172 buffer = (*ds->CreateBuffer)(pDraw, attachment, format); 173 } else { 174 buffer = pPriv->buffers[old_buf]; 175 pPriv->buffers[old_buf] = NULL; 176 } 177 178 return buffer; 179} 180 181static DRI2Buffer2Ptr * 182do_get_buffers(DrawablePtr pDraw, int *width, int *height, 183 unsigned int *attachments, int count, int *out_count, 184 int has_format) 185{ 186 DRI2ScreenPtr ds = DRI2GetScreen(pDraw->pScreen); 187 DRI2DrawablePtr pPriv = DRI2GetDrawable(pDraw); 188 DRI2Buffer2Ptr *buffers; 189 int need_real_front = 0; 190 int need_fake_front = 0; 191 int have_fake_front = 0; 192 int front_format = 0; 193 const int dimensions_match = (pDraw->width == pPriv->width) 194 && (pDraw->height == pPriv->height); 195 int i; 196 197 198 buffers = xalloc((count + 1) * sizeof(buffers[0])); 199 200 if (ds->CreateBuffer) { 201 /* Version 2 API with CreateBuffer */ 202 for (i = 0; i < count; i++) { 203 const unsigned attachment = *(attachments++); 204 const unsigned format = (has_format) ? *(attachments++) : 0; 205 206 buffers[i] = allocate_or_reuse_buffer(pDraw, ds, pPriv, attachment, 207 format, dimensions_match); 208 209 /* If the drawable is a window and the front-buffer is requested, 210 * silently add the fake front-buffer to the list of requested 211 * attachments. The counting logic in the loop accounts for the case 212 * where the client requests both the fake and real front-buffer. 213 */ 214 if (attachment == DRI2BufferBackLeft) { 215 need_real_front++; 216 front_format = format; 217 } 218 219 if (attachment == DRI2BufferFrontLeft) { 220 need_real_front--; 221 front_format = format; 222 223 if (pDraw->type == DRAWABLE_WINDOW) { 224 need_fake_front++; 225 } 226 } 227 228 if (pDraw->type == DRAWABLE_WINDOW) { 229 if (attachment == DRI2BufferFakeFrontLeft) { 230 need_fake_front--; 231 have_fake_front = 1; 232 } 233 } 234 } 235 236 if (need_real_front > 0) { 237 buffers[i++] = allocate_or_reuse_buffer(pDraw, ds, pPriv, 238 DRI2BufferFrontLeft, 239 front_format, dimensions_match); 240 } 241 242 if (need_fake_front > 0) { 243 buffers[i++] = allocate_or_reuse_buffer(pDraw, ds, pPriv, 244 DRI2BufferFakeFrontLeft, 245 front_format, dimensions_match); 246 have_fake_front = 1; 247 } 248 249 *out_count = i; 250 251 252 if (pPriv->buffers != NULL) { 253 for (i = 0; i < pPriv->bufferCount; i++) { 254 if (pPriv->buffers[i] != NULL) { 255 (*ds->DestroyBuffer)(pDraw, pPriv->buffers[i]); 256 } 257 } 258 259 xfree(pPriv->buffers); 260 } 261 } else { 262 DRI2BufferPtr buffers1; 263 unsigned int temp_buf[32]; 264 unsigned int *temp = temp_buf; 265 int i; 266 int buffers_match = 1; 267 268 /* Version 1 API with CreateBuffers */ 269 270 if ((count + 1) > 32) { 271 temp = xalloc((count + 1) * sizeof(temp[0])); 272 } 273 274 for (i = 0; i < count; i++) { 275 const unsigned attachment = *(attachments++); 276 277 /* Version 1 doesn't deal with the format at all */ 278 if (has_format) 279 attachments++; 280 281 /* 282 * Make sure the client also gets the front buffer when 283 * it asks for a back buffer 284 */ 285 if (attachment == DRI2BufferBackLeft) 286 need_real_front++; 287 288 /* 289 * If the drawable is a window and the front-buffer is requested, 290 * silently add the fake front-buffer to the list of requested 291 * attachments. The counting logic in the loop accounts for the 292 * case where the client requests both the fake and real 293 * front-buffer. 294 */ 295 if (attachment == DRI2BufferFrontLeft) { 296 need_real_front--; 297 if (pDraw->type == DRAWABLE_WINDOW) 298 need_fake_front++; 299 } 300 if (pDraw->type == DRAWABLE_WINDOW && 301 attachment == DRI2BufferFakeFrontLeft) 302 { 303 need_fake_front--; 304 have_fake_front = 1; 305 } 306 307 temp[i] = attachment; 308 } 309 310 if (need_real_front > 0) 311 temp[count++] = DRI2BufferFrontLeft; 312 313 if (need_fake_front > 0) { 314 temp[count++] = DRI2BufferFakeFrontLeft; 315 have_fake_front = 1; 316 } 317 318 if (count != pPriv->bufferCount) 319 buffers_match = 0; 320 else { 321 for (i = 0; i < count; i++) 322 if (pPriv->buffers[i]->attachment != temp[i]) { 323 buffers_match = 0; 324 break; 325 } 326 } 327 if (pPriv->buffers == NULL || !dimensions_match || !buffers_match) 328 { 329 buffers1 = (*ds->CreateBuffers)(pDraw, temp, count); 330 if (pPriv->buffers != NULL) 331 (*ds->DestroyBuffers)(pDraw, (DRI2BufferPtr) pPriv->buffers[0], 332 pPriv->bufferCount); 333 } 334 else 335 buffers1 = (DRI2BufferPtr) pPriv->buffers[0]; 336 337 for (i = 0; i < count; i++) 338 buffers[i] = (DRI2Buffer2Ptr) &buffers1[i]; 339 340 *out_count = count; 341 342 if (pPriv->buffers) 343 xfree (pPriv->buffers); 344 345 if (temp != temp_buf) { 346 xfree(temp); 347 } 348 } 349 350 pPriv->buffers = buffers; 351 pPriv->bufferCount = *out_count; 352 pPriv->width = pDraw->width; 353 pPriv->height = pDraw->height; 354 *width = pPriv->width; 355 *height = pPriv->height; 356 357 358 /* If the client is getting a fake front-buffer, pre-fill it with the 359 * contents of the real front-buffer. This ensures correct operation of 360 * applications that call glXWaitX before calling glDrawBuffer. 361 */ 362 if (have_fake_front) { 363 BoxRec box; 364 RegionRec region; 365 366 box.x1 = 0; 367 box.y1 = 0; 368 box.x2 = pPriv->width; 369 box.y2 = pPriv->height; 370 REGION_INIT(pDraw->pScreen, ®ion, &box, 0); 371 372 DRI2CopyRegion(pDraw, ®ion, DRI2BufferFakeFrontLeft, 373 DRI2BufferFrontLeft); 374 } 375 376 return pPriv->buffers; 377} 378 379DRI2Buffer2Ptr * 380DRI2GetBuffers(DrawablePtr pDraw, int *width, int *height, 381 unsigned int *attachments, int count, int *out_count) 382{ 383 return do_get_buffers(pDraw, width, height, attachments, count, 384 out_count, FALSE); 385} 386 387DRI2Buffer2Ptr * 388DRI2GetBuffersWithFormat(DrawablePtr pDraw, int *width, int *height, 389 unsigned int *attachments, int count, int *out_count) 390{ 391 return do_get_buffers(pDraw, width, height, attachments, count, 392 out_count, TRUE); 393} 394 395int 396DRI2CopyRegion(DrawablePtr pDraw, RegionPtr pRegion, 397 unsigned int dest, unsigned int src) 398{ 399 DRI2ScreenPtr ds = DRI2GetScreen(pDraw->pScreen); 400 DRI2DrawablePtr pPriv; 401 DRI2BufferPtr pDestBuffer, pSrcBuffer; 402 int i; 403 404 pPriv = DRI2GetDrawable(pDraw); 405 if (pPriv == NULL) 406 return BadDrawable; 407 408 pDestBuffer = NULL; 409 pSrcBuffer = NULL; 410 for (i = 0; i < pPriv->bufferCount; i++) 411 { 412 if (pPriv->buffers[i]->attachment == dest) 413 pDestBuffer = (DRI2BufferPtr) pPriv->buffers[i]; 414 if (pPriv->buffers[i]->attachment == src) 415 pSrcBuffer = (DRI2BufferPtr) pPriv->buffers[i]; 416 } 417 if (pSrcBuffer == NULL || pDestBuffer == NULL) 418 return BadValue; 419 420 (*ds->CopyRegion)(pDraw, pRegion, pDestBuffer, pSrcBuffer); 421 422 return Success; 423} 424 425void 426DRI2DestroyDrawable(DrawablePtr pDraw) 427{ 428 DRI2ScreenPtr ds = DRI2GetScreen(pDraw->pScreen); 429 DRI2DrawablePtr pPriv; 430 WindowPtr pWin; 431 PixmapPtr pPixmap; 432 433 pPriv = DRI2GetDrawable(pDraw); 434 if (pPriv == NULL) 435 return; 436 437 pPriv->refCount--; 438 if (pPriv->refCount > 0) 439 return; 440 441 if (pPriv->buffers != NULL) { 442 int i; 443 444 if (ds->DestroyBuffer) { 445 for (i = 0; i < pPriv->bufferCount; i++) { 446 (*ds->DestroyBuffer)(pDraw, pPriv->buffers[i]); 447 } 448 } else { 449 (*ds->DestroyBuffers)(pDraw, (DRI2BufferPtr) pPriv->buffers[0], 450 pPriv->bufferCount); 451 } 452 453 xfree(pPriv->buffers); 454 } 455 456 xfree(pPriv); 457 458 if (pDraw->type == DRAWABLE_WINDOW) 459 { 460 pWin = (WindowPtr) pDraw; 461 dixSetPrivate(&pWin->devPrivates, dri2WindowPrivateKey, NULL); 462 } 463 else 464 { 465 pPixmap = (PixmapPtr) pDraw; 466 dixSetPrivate(&pPixmap->devPrivates, dri2PixmapPrivateKey, NULL); 467 } 468} 469 470Bool 471DRI2Connect(ScreenPtr pScreen, unsigned int driverType, int *fd, 472 const char **driverName, const char **deviceName) 473{ 474 DRI2ScreenPtr ds = DRI2GetScreen(pScreen); 475 476 if (ds == NULL) 477 return FALSE; 478 479 if (driverType != DRI2DriverDRI) 480 return BadValue; 481 482 *fd = ds->fd; 483 *driverName = ds->driverName; 484 *deviceName = ds->deviceName; 485 486 return TRUE; 487} 488 489Bool 490DRI2Authenticate(ScreenPtr pScreen, drm_magic_t magic) 491{ 492 DRI2ScreenPtr ds = DRI2GetScreen(pScreen); 493 494 if (ds == NULL || drmAuthMagic(ds->fd, magic)) 495 return FALSE; 496 497 return TRUE; 498} 499 500Bool 501DRI2ScreenInit(ScreenPtr pScreen, DRI2InfoPtr info) 502{ 503 DRI2ScreenPtr ds; 504 505 ds = xalloc(sizeof *ds); 506 if (!ds) 507 return FALSE; 508 509 ds->fd = info->fd; 510 ds->driverName = info->driverName; 511 ds->deviceName = info->deviceName; 512 513 /* Prefer the new one-at-a-time buffer API */ 514 if (info->version >= 2 && info->CreateBuffer && info->DestroyBuffer) { 515 ds->CreateBuffer = info->CreateBuffer; 516 ds->DestroyBuffer = info->DestroyBuffer; 517 ds->CreateBuffers = NULL; 518 ds->DestroyBuffers = NULL; 519 } else if (info->CreateBuffers && info->DestroyBuffers) { 520 xf86DrvMsg(pScreen->myNum, X_WARNING, 521 "[DRI2] Version 1 API (broken front buffer rendering)\n"); 522 ds->CreateBuffer = NULL; 523 ds->DestroyBuffer = NULL; 524 ds->CreateBuffers = info->CreateBuffers; 525 ds->DestroyBuffers = info->DestroyBuffers; 526 } else { 527 xf86DrvMsg(pScreen->myNum, X_ERROR, 528 "[DRI2] Missing buffer management functions\n"); 529 xfree(ds); 530 return FALSE; 531 } 532 533 if (!info->CopyRegion) { 534 xf86DrvMsg(pScreen->myNum, X_ERROR, 535 "[DRI2] Missing copy region function\n"); 536 xfree(ds); 537 return FALSE; 538 } 539 ds->CopyRegion = info->CopyRegion; 540 541 dixSetPrivate(&pScreen->devPrivates, dri2ScreenPrivateKey, ds); 542 543 xf86DrvMsg(pScreen->myNum, X_INFO, "[DRI2] Setup complete\n"); 544 545 return TRUE; 546} 547 548void 549DRI2CloseScreen(ScreenPtr pScreen) 550{ 551 DRI2ScreenPtr ds = DRI2GetScreen(pScreen); 552 553 xfree(ds); 554 dixSetPrivate(&pScreen->devPrivates, dri2ScreenPrivateKey, NULL); 555} 556 557extern ExtensionModule dri2ExtensionModule; 558 559static pointer 560DRI2Setup(pointer module, pointer opts, int *errmaj, int *errmin) 561{ 562 static Bool setupDone = FALSE; 563 564 if (!setupDone) 565 { 566 setupDone = TRUE; 567 LoadExtension(&dri2ExtensionModule, FALSE); 568 } 569 else 570 { 571 if (errmaj) 572 *errmaj = LDR_ONCEONLY; 573 } 574 575 return (pointer) 1; 576} 577 578static XF86ModuleVersionInfo DRI2VersRec = 579{ 580 "dri2", 581 MODULEVENDORSTRING, 582 MODINFOSTRING1, 583 MODINFOSTRING2, 584 XORG_VERSION_CURRENT, 585 1, 1, 0, 586 ABI_CLASS_EXTENSION, 587 ABI_EXTENSION_VERSION, 588 MOD_CLASS_NONE, 589 { 0, 0, 0, 0 } 590}; 591 592_X_EXPORT XF86ModuleData dri2ModuleData = { &DRI2VersRec, DRI2Setup, NULL }; 593 594void 595DRI2Version(int *major, int *minor) 596{ 597 if (major != NULL) 598 *major = DRI2VersRec.majorversion; 599 600 if (minor != NULL) 601 *minor = DRI2VersRec.minorversion; 602} 603