dri2.c revision b1d344b3
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 int dimensions_match; 194 int i; 195 196 if (!pPriv) { 197 *width = pDraw->width; 198 *height = pDraw->height; 199 *out_count = 0; 200 return NULL; 201 } 202 203 dimensions_match = (pDraw->width == pPriv->width) 204 && (pDraw->height == pPriv->height); 205 206 buffers = xalloc((count + 1) * sizeof(buffers[0])); 207 208 if (ds->CreateBuffer) { 209 /* Version 2 API with CreateBuffer */ 210 for (i = 0; i < count; i++) { 211 const unsigned attachment = *(attachments++); 212 const unsigned format = (has_format) ? *(attachments++) : 0; 213 214 buffers[i] = allocate_or_reuse_buffer(pDraw, ds, pPriv, attachment, 215 format, dimensions_match); 216 217 /* If the drawable is a window and the front-buffer is requested, 218 * silently add the fake front-buffer to the list of requested 219 * attachments. The counting logic in the loop accounts for the case 220 * where the client requests both the fake and real front-buffer. 221 */ 222 if (attachment == DRI2BufferBackLeft) { 223 need_real_front++; 224 front_format = format; 225 } 226 227 if (attachment == DRI2BufferFrontLeft) { 228 need_real_front--; 229 front_format = format; 230 231 if (pDraw->type == DRAWABLE_WINDOW) { 232 need_fake_front++; 233 } 234 } 235 236 if (pDraw->type == DRAWABLE_WINDOW) { 237 if (attachment == DRI2BufferFakeFrontLeft) { 238 need_fake_front--; 239 have_fake_front = 1; 240 } 241 } 242 } 243 244 if (need_real_front > 0) { 245 buffers[i++] = allocate_or_reuse_buffer(pDraw, ds, pPriv, 246 DRI2BufferFrontLeft, 247 front_format, dimensions_match); 248 } 249 250 if (need_fake_front > 0) { 251 buffers[i++] = allocate_or_reuse_buffer(pDraw, ds, pPriv, 252 DRI2BufferFakeFrontLeft, 253 front_format, dimensions_match); 254 have_fake_front = 1; 255 } 256 257 *out_count = i; 258 259 260 if (pPriv->buffers != NULL) { 261 for (i = 0; i < pPriv->bufferCount; i++) { 262 if (pPriv->buffers[i] != NULL) { 263 (*ds->DestroyBuffer)(pDraw, pPriv->buffers[i]); 264 } 265 } 266 267 xfree(pPriv->buffers); 268 } 269 } else { 270 DRI2BufferPtr buffers1; 271 unsigned int temp_buf[32]; 272 unsigned int *temp = temp_buf; 273 int i; 274 int buffers_match = 1; 275 276 /* Version 1 API with CreateBuffers */ 277 278 if ((count + 1) > 32) { 279 temp = xalloc((count + 1) * sizeof(temp[0])); 280 } 281 282 for (i = 0; i < count; i++) { 283 const unsigned attachment = *(attachments++); 284 285 /* Version 1 doesn't deal with the format at all */ 286 if (has_format) 287 attachments++; 288 289 /* 290 * Make sure the client also gets the front buffer when 291 * it asks for a back buffer 292 */ 293 if (attachment == DRI2BufferBackLeft) 294 need_real_front++; 295 296 /* 297 * If the drawable is a window and the front-buffer is requested, 298 * silently add the fake front-buffer to the list of requested 299 * attachments. The counting logic in the loop accounts for the 300 * case where the client requests both the fake and real 301 * front-buffer. 302 */ 303 if (attachment == DRI2BufferFrontLeft) { 304 need_real_front--; 305 if (pDraw->type == DRAWABLE_WINDOW) 306 need_fake_front++; 307 } 308 if (pDraw->type == DRAWABLE_WINDOW && 309 attachment == DRI2BufferFakeFrontLeft) 310 { 311 need_fake_front--; 312 have_fake_front = 1; 313 } 314 315 temp[i] = attachment; 316 } 317 318 if (need_real_front > 0) 319 temp[count++] = DRI2BufferFrontLeft; 320 321 if (need_fake_front > 0) { 322 temp[count++] = DRI2BufferFakeFrontLeft; 323 have_fake_front = 1; 324 } 325 326 if (count != pPriv->bufferCount) 327 buffers_match = 0; 328 else { 329 for (i = 0; i < count; i++) 330 if (pPriv->buffers[i]->attachment != temp[i]) { 331 buffers_match = 0; 332 break; 333 } 334 } 335 if (pPriv->buffers == NULL || !dimensions_match || !buffers_match) 336 { 337 buffers1 = (*ds->CreateBuffers)(pDraw, temp, count); 338 if (pPriv->buffers != NULL) 339 (*ds->DestroyBuffers)(pDraw, (DRI2BufferPtr) pPriv->buffers[0], 340 pPriv->bufferCount); 341 } 342 else 343 buffers1 = (DRI2BufferPtr) pPriv->buffers[0]; 344 345 for (i = 0; i < count; i++) 346 buffers[i] = (DRI2Buffer2Ptr) &buffers1[i]; 347 348 *out_count = count; 349 350 if (pPriv->buffers) 351 xfree (pPriv->buffers); 352 353 if (temp != temp_buf) { 354 xfree(temp); 355 } 356 } 357 358 pPriv->buffers = buffers; 359 pPriv->bufferCount = *out_count; 360 pPriv->width = pDraw->width; 361 pPriv->height = pDraw->height; 362 *width = pPriv->width; 363 *height = pPriv->height; 364 365 366 /* If the client is getting a fake front-buffer, pre-fill it with the 367 * contents of the real front-buffer. This ensures correct operation of 368 * applications that call glXWaitX before calling glDrawBuffer. 369 */ 370 if (have_fake_front) { 371 BoxRec box; 372 RegionRec region; 373 374 box.x1 = 0; 375 box.y1 = 0; 376 box.x2 = pPriv->width; 377 box.y2 = pPriv->height; 378 REGION_INIT(pDraw->pScreen, ®ion, &box, 0); 379 380 DRI2CopyRegion(pDraw, ®ion, DRI2BufferFakeFrontLeft, 381 DRI2BufferFrontLeft); 382 } 383 384 return pPriv->buffers; 385} 386 387DRI2Buffer2Ptr * 388DRI2GetBuffers(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, FALSE); 393} 394 395DRI2Buffer2Ptr * 396DRI2GetBuffersWithFormat(DrawablePtr pDraw, int *width, int *height, 397 unsigned int *attachments, int count, int *out_count) 398{ 399 return do_get_buffers(pDraw, width, height, attachments, count, 400 out_count, TRUE); 401} 402 403int 404DRI2CopyRegion(DrawablePtr pDraw, RegionPtr pRegion, 405 unsigned int dest, unsigned int src) 406{ 407 DRI2ScreenPtr ds = DRI2GetScreen(pDraw->pScreen); 408 DRI2DrawablePtr pPriv; 409 DRI2BufferPtr pDestBuffer, pSrcBuffer; 410 int i; 411 412 pPriv = DRI2GetDrawable(pDraw); 413 if (pPriv == NULL) 414 return BadDrawable; 415 416 pDestBuffer = NULL; 417 pSrcBuffer = NULL; 418 for (i = 0; i < pPriv->bufferCount; i++) 419 { 420 if (pPriv->buffers[i]->attachment == dest) 421 pDestBuffer = (DRI2BufferPtr) pPriv->buffers[i]; 422 if (pPriv->buffers[i]->attachment == src) 423 pSrcBuffer = (DRI2BufferPtr) pPriv->buffers[i]; 424 } 425 if (pSrcBuffer == NULL || pDestBuffer == NULL) 426 return BadValue; 427 428 (*ds->CopyRegion)(pDraw, pRegion, pDestBuffer, pSrcBuffer); 429 430 return Success; 431} 432 433void 434DRI2DestroyDrawable(DrawablePtr pDraw) 435{ 436 DRI2ScreenPtr ds = DRI2GetScreen(pDraw->pScreen); 437 DRI2DrawablePtr pPriv; 438 WindowPtr pWin; 439 PixmapPtr pPixmap; 440 441 pPriv = DRI2GetDrawable(pDraw); 442 if (pPriv == NULL) 443 return; 444 445 pPriv->refCount--; 446 if (pPriv->refCount > 0) 447 return; 448 449 if (pPriv->buffers != NULL) { 450 int i; 451 452 if (ds->DestroyBuffer) { 453 for (i = 0; i < pPriv->bufferCount; i++) { 454 (*ds->DestroyBuffer)(pDraw, pPriv->buffers[i]); 455 } 456 } else { 457 (*ds->DestroyBuffers)(pDraw, (DRI2BufferPtr) pPriv->buffers[0], 458 pPriv->bufferCount); 459 } 460 461 xfree(pPriv->buffers); 462 } 463 464 xfree(pPriv); 465 466 if (pDraw->type == DRAWABLE_WINDOW) 467 { 468 pWin = (WindowPtr) pDraw; 469 dixSetPrivate(&pWin->devPrivates, dri2WindowPrivateKey, NULL); 470 } 471 else 472 { 473 pPixmap = (PixmapPtr) pDraw; 474 dixSetPrivate(&pPixmap->devPrivates, dri2PixmapPrivateKey, NULL); 475 } 476} 477 478Bool 479DRI2Connect(ScreenPtr pScreen, unsigned int driverType, int *fd, 480 const char **driverName, const char **deviceName) 481{ 482 DRI2ScreenPtr ds = DRI2GetScreen(pScreen); 483 484 if (ds == NULL) 485 return FALSE; 486 487 if (driverType != DRI2DriverDRI) 488 return BadValue; 489 490 *fd = ds->fd; 491 *driverName = ds->driverName; 492 *deviceName = ds->deviceName; 493 494 return TRUE; 495} 496 497Bool 498DRI2Authenticate(ScreenPtr pScreen, drm_magic_t magic) 499{ 500 DRI2ScreenPtr ds = DRI2GetScreen(pScreen); 501 502 if (ds == NULL || drmAuthMagic(ds->fd, magic)) 503 return FALSE; 504 505 return TRUE; 506} 507 508Bool 509DRI2ScreenInit(ScreenPtr pScreen, DRI2InfoPtr info) 510{ 511 DRI2ScreenPtr ds; 512 513 ds = xalloc(sizeof *ds); 514 if (!ds) 515 return FALSE; 516 517 ds->fd = info->fd; 518 ds->driverName = info->driverName; 519 ds->deviceName = info->deviceName; 520 521 /* Prefer the new one-at-a-time buffer API */ 522 if (info->version >= 2 && info->CreateBuffer && info->DestroyBuffer) { 523 ds->CreateBuffer = info->CreateBuffer; 524 ds->DestroyBuffer = info->DestroyBuffer; 525 ds->CreateBuffers = NULL; 526 ds->DestroyBuffers = NULL; 527 } else if (info->CreateBuffers && info->DestroyBuffers) { 528 xf86DrvMsg(pScreen->myNum, X_WARNING, 529 "[DRI2] Version 1 API (broken front buffer rendering)\n"); 530 ds->CreateBuffer = NULL; 531 ds->DestroyBuffer = NULL; 532 ds->CreateBuffers = info->CreateBuffers; 533 ds->DestroyBuffers = info->DestroyBuffers; 534 } else { 535 xf86DrvMsg(pScreen->myNum, X_ERROR, 536 "[DRI2] Missing buffer management functions\n"); 537 xfree(ds); 538 return FALSE; 539 } 540 541 if (!info->CopyRegion) { 542 xf86DrvMsg(pScreen->myNum, X_ERROR, 543 "[DRI2] Missing copy region function\n"); 544 xfree(ds); 545 return FALSE; 546 } 547 ds->CopyRegion = info->CopyRegion; 548 549 dixSetPrivate(&pScreen->devPrivates, dri2ScreenPrivateKey, ds); 550 551 xf86DrvMsg(pScreen->myNum, X_INFO, "[DRI2] Setup complete\n"); 552 553 return TRUE; 554} 555 556void 557DRI2CloseScreen(ScreenPtr pScreen) 558{ 559 DRI2ScreenPtr ds = DRI2GetScreen(pScreen); 560 561 xfree(ds); 562 dixSetPrivate(&pScreen->devPrivates, dri2ScreenPrivateKey, NULL); 563} 564 565extern ExtensionModule dri2ExtensionModule; 566 567static pointer 568DRI2Setup(pointer module, pointer opts, int *errmaj, int *errmin) 569{ 570 static Bool setupDone = FALSE; 571 572 if (!setupDone) 573 { 574 setupDone = TRUE; 575 LoadExtension(&dri2ExtensionModule, FALSE); 576 } 577 else 578 { 579 if (errmaj) 580 *errmaj = LDR_ONCEONLY; 581 } 582 583 return (pointer) 1; 584} 585 586static XF86ModuleVersionInfo DRI2VersRec = 587{ 588 "dri2", 589 MODULEVENDORSTRING, 590 MODINFOSTRING1, 591 MODINFOSTRING2, 592 XORG_VERSION_CURRENT, 593 1, 1, 0, 594 ABI_CLASS_EXTENSION, 595 ABI_EXTENSION_VERSION, 596 MOD_CLASS_NONE, 597 { 0, 0, 0, 0 } 598}; 599 600_X_EXPORT XF86ModuleData dri2ModuleData = { &DRI2VersRec, DRI2Setup, NULL }; 601 602void 603DRI2Version(int *major, int *minor) 604{ 605 if (major != NULL) 606 *major = DRI2VersRec.majorversion; 607 608 if (minor != NULL) 609 *minor = DRI2VersRec.minorversion; 610} 611