xorg_dri2.c revision d12f2bb9c03a9e8a08824c849200f5b23c05914c
1/* 2 * Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas. 3 * All Rights Reserved. 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a 6 * copy of this software and associated documentation files (the 7 * "Software"), to deal in the Software without restriction, including 8 * without limitation the rights to use, copy, modify, merge, publish, 9 * distribute, sub license, and/or sell copies of the Software, and to 10 * permit persons to whom the Software is furnished to do so, subject to 11 * the following conditions: 12 * 13 * The above copyright notice and this permission notice (including the 14 * next paragraph) shall be included in all copies or substantial portions 15 * of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 20 * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR 21 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 * 25 * 26 * Author: Alan Hourihane <alanh@tungstengraphics.com> 27 * Author: Jakob Bornecrantz <wallbraker@gmail.com> 28 * 29 */ 30 31#include "xorg-server.h" 32#include "xf86.h" 33#include "xf86_OSproc.h" 34 35#include "xorg_tracker.h" 36#include "xorg_exa.h" 37 38#include "dri2.h" 39 40#include "pipe/p_state.h" 41#include "util/u_inlines.h" 42 43#include "util/u_format.h" 44 45/* Make all the #if cases in the code esier to read */ 46#ifndef DRI2INFOREC_VERSION 47#define DRI2INFOREC_VERSION 1 48#endif 49 50#if DRI2INFOREC_VERSION == 2 51static Bool set_format_in_do_create_buffer; 52#endif 53 54typedef struct { 55 PixmapPtr pPixmap; 56 struct pipe_resource *tex; 57 struct pipe_fence_handle *fence; 58} *BufferPrivatePtr; 59 60static Bool 61dri2_do_create_buffer(DrawablePtr pDraw, DRI2BufferPtr buffer, unsigned int format) 62{ 63 struct pipe_resource *tex = NULL; 64 ScreenPtr pScreen = pDraw->pScreen; 65 ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; 66 modesettingPtr ms = modesettingPTR(pScrn); 67 struct exa_pixmap_priv *exa_priv; 68 BufferPrivatePtr private = buffer->driverPrivate; 69 PixmapPtr pPixmap; 70 struct winsys_handle whandle; 71 72 if (pDraw->type == DRAWABLE_PIXMAP) 73 pPixmap = (PixmapPtr) pDraw; 74 else 75 pPixmap = (*pScreen->GetWindowPixmap)((WindowPtr) pDraw); 76 exa_priv = exaGetPixmapDriverPrivate(pPixmap); 77 78 79 switch (buffer->attachment) { 80 default: 81 if (buffer->attachment != DRI2BufferFakeFrontLeft || 82 pDraw->type != DRAWABLE_PIXMAP) { 83 private->pPixmap = (*pScreen->CreatePixmap)(pScreen, pDraw->width, 84 pDraw->height, 85 pDraw->depth, 86 0); 87 } 88 break; 89 case DRI2BufferFrontLeft: 90 break; 91 case DRI2BufferStencil: 92#if DRI2INFOREC_VERSION >= 3 93 case DRI2BufferDepthStencil: 94#else 95 /* Works on old X servers because sanity checking is for the weak */ 96 case 9: 97#endif 98 if (exa_priv->depth_stencil_tex && 99 !util_format_is_depth_or_stencil(exa_priv->depth_stencil_tex->format)) 100 exa_priv->depth_stencil_tex = NULL; 101 /* Fall through */ 102 case DRI2BufferDepth: 103 if (exa_priv->depth_stencil_tex) 104 pipe_resource_reference(&tex, exa_priv->depth_stencil_tex); 105 else { 106 struct pipe_resource template; 107 unsigned depthBits = (format != 0) ? format : pDraw->depth; 108 memset(&template, 0, sizeof(template)); 109 template.target = PIPE_TEXTURE_2D; 110 if (buffer->attachment == DRI2BufferDepth) { 111 switch(depthBits) { 112 case 16: 113 template.format = PIPE_FORMAT_Z16_UNORM; 114 break; 115 case 32: 116 template.format = PIPE_FORMAT_Z32_UNORM; 117 break; 118 default: 119 template.format = ms->ds_depth_bits_last ? 120 PIPE_FORMAT_Z24X8_UNORM : PIPE_FORMAT_X8Z24_UNORM; 121 break; 122 } 123 } else { 124 template.format = ms->ds_depth_bits_last ? 125 PIPE_FORMAT_Z24_UNORM_S8_USCALED : PIPE_FORMAT_S8_USCALED_Z24_UNORM; 126 } 127 template.width0 = pDraw->width; 128 template.height0 = pDraw->height; 129 template.depth0 = 1; 130 template.last_level = 0; 131 template.bind = PIPE_BIND_DEPTH_STENCIL | 132 PIPE_BIND_SHARED; 133 tex = ms->screen->resource_create(ms->screen, &template); 134 pipe_resource_reference(&exa_priv->depth_stencil_tex, tex); 135 } 136 break; 137 } 138 139 if (!private->pPixmap) { 140 private->pPixmap = pPixmap; 141 pPixmap->refcnt++; 142 } 143 144 if (!tex) { 145 /* First call to make sure we have a pixmap private */ 146 exaMoveInPixmap(private->pPixmap); 147 xorg_exa_set_shared_usage(private->pPixmap); 148 pScreen->ModifyPixmapHeader(private->pPixmap, 0, 0, 0, 0, 0, NULL); 149 /* Second call to make sure texture has valid contents */ 150 exaMoveInPixmap(private->pPixmap); 151 tex = xorg_exa_get_texture(private->pPixmap); 152 } 153 154 if (!tex) 155 FatalError("NO TEXTURE IN DRI2\n"); 156 157 memset(&whandle, 0, sizeof(whandle)); 158 whandle.type = DRM_API_HANDLE_TYPE_SHARED; 159 160 ms->screen->resource_get_handle(ms->screen, tex, &whandle); 161 162 buffer->name = whandle.handle; 163 buffer->pitch = whandle.stride; 164 buffer->cpp = 4; 165 buffer->driverPrivate = private; 166 buffer->flags = 0; /* not tiled */ 167#if DRI2INFOREC_VERSION == 2 168 /* ABI forwards/backwards compatibility */ 169 if (set_format_in_do_create_buffer) 170 ((DRI2Buffer2Ptr)buffer)->format = 0; 171#elif DRI2INFOREC_VERSION >= 3 172 buffer->format = 0; 173#endif 174 private->tex = tex; 175 176 return TRUE; 177} 178 179static void 180dri2_do_destroy_buffer(DrawablePtr pDraw, DRI2BufferPtr buffer) 181{ 182 ScreenPtr pScreen = pDraw->pScreen; 183 ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; 184 modesettingPtr ms = modesettingPTR(pScrn); 185 BufferPrivatePtr private = buffer->driverPrivate; 186 struct exa_pixmap_priv *exa_priv = exaGetPixmapDriverPrivate(private->pPixmap); 187 188 pipe_resource_reference(&private->tex, NULL); 189 ms->screen->fence_reference(ms->screen, &private->fence, NULL); 190 pipe_resource_reference(&exa_priv->depth_stencil_tex, NULL); 191 (*pScreen->DestroyPixmap)(private->pPixmap); 192} 193 194#if DRI2INFOREC_VERSION >= 2 195 196static DRI2Buffer2Ptr 197dri2_create_buffer(DrawablePtr pDraw, unsigned int attachment, unsigned int format) 198{ 199 DRI2Buffer2Ptr buffer; 200 BufferPrivatePtr private; 201 202 buffer = xcalloc(1, sizeof *buffer); 203 if (!buffer) 204 return NULL; 205 206 private = xcalloc(1, sizeof *private); 207 if (!private) { 208 goto fail; 209 } 210 211 buffer->attachment = attachment; 212 buffer->driverPrivate = private; 213 214 /* So far it is safe to downcast a DRI2Buffer2Ptr to DRI2BufferPtr */ 215 if (dri2_do_create_buffer(pDraw, (DRI2BufferPtr)buffer, format)) 216 return buffer; 217 218 xfree(private); 219fail: 220 xfree(buffer); 221 return NULL; 222} 223 224static void 225dri2_destroy_buffer(DrawablePtr pDraw, DRI2Buffer2Ptr buffer) 226{ 227 /* So far it is safe to downcast a DRI2Buffer2Ptr to DRI2BufferPtr */ 228 dri2_do_destroy_buffer(pDraw, (DRI2BufferPtr)buffer); 229 230 xfree(buffer->driverPrivate); 231 xfree(buffer); 232} 233 234#endif /* DRI2INFOREC_VERSION >= 2 */ 235 236#if DRI2INFOREC_VERSION <= 2 237 238static DRI2BufferPtr 239dri2_create_buffers(DrawablePtr pDraw, unsigned int *attachments, int count) 240{ 241 BufferPrivatePtr privates; 242 DRI2BufferPtr buffers; 243 int i; 244 245 buffers = xcalloc(count, sizeof *buffers); 246 if (!buffers) 247 goto fail_buffers; 248 249 privates = xcalloc(count, sizeof *privates); 250 if (!privates) 251 goto fail_privates; 252 253 for (i = 0; i < count; i++) { 254 buffers[i].attachment = attachments[i]; 255 buffers[i].driverPrivate = &privates[i]; 256 257 if (!dri2_do_create_buffer(pDraw, &buffers[i], 0)) 258 goto fail; 259 } 260 261 return buffers; 262 263fail: 264 xfree(privates); 265fail_privates: 266 xfree(buffers); 267fail_buffers: 268 return NULL; 269} 270 271static void 272dri2_destroy_buffers(DrawablePtr pDraw, DRI2BufferPtr buffers, int count) 273{ 274 int i; 275 276 for (i = 0; i < count; i++) { 277 dri2_do_destroy_buffer(pDraw, &buffers[i]); 278 } 279 280 if (buffers) { 281 xfree(buffers[0].driverPrivate); 282 xfree(buffers); 283 } 284} 285 286#endif /* DRI2INFOREC_VERSION <= 2 */ 287 288static void 289dri2_copy_region(DrawablePtr pDraw, RegionPtr pRegion, 290 DRI2BufferPtr pDestBuffer, DRI2BufferPtr pSrcBuffer) 291{ 292 ScreenPtr pScreen = pDraw->pScreen; 293 ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; 294 modesettingPtr ms = modesettingPTR(pScrn); 295 BufferPrivatePtr dst_priv = pDestBuffer->driverPrivate; 296 BufferPrivatePtr src_priv = pSrcBuffer->driverPrivate; 297 DrawablePtr src_draw; 298 DrawablePtr dst_draw; 299 GCPtr gc; 300 RegionPtr copy_clip; 301 Bool save_accel; 302 CustomizerPtr cust = ms->cust; 303 304 /* 305 * In driCreateBuffers we dewrap windows into the 306 * backing pixmaps in order to get to the texture. 307 * We need to use the real drawable in CopyArea 308 * so that cliprects and offsets are correct. 309 */ 310 src_draw = (pSrcBuffer->attachment == DRI2BufferFrontLeft) ? pDraw : 311 &src_priv->pPixmap->drawable; 312 dst_draw = (pDestBuffer->attachment == DRI2BufferFrontLeft) ? pDraw : 313 &dst_priv->pPixmap->drawable; 314 315 /* 316 * The clients implements glXWaitX with a copy front to fake and then 317 * waiting on the server to signal its completion of it. While 318 * glXWaitGL is a client side flush and a copy from fake to front. 319 * This is how it is done in the DRI2 protocol, how ever depending 320 * which type of drawables the server does things a bit differently 321 * then what the protocol says as the fake and front are the same. 322 * 323 * for pixmaps glXWaitX is a server flush. 324 * for pixmaps glXWaitGL is a client flush. 325 * for windows glXWaitX is a copy from front to fake then a server flush. 326 * for windows glXWaitGL is a client flush then a copy from fake to front. 327 * 328 * XXX in the windows case this code always flushes but that isn't a 329 * must in the glXWaitGL case but we don't know if this is a glXWaitGL 330 * or a glFlush/glFinish call. 331 */ 332 if (dst_priv->pPixmap == src_priv->pPixmap) { 333 /* pixmap glXWaitX */ 334 if (pSrcBuffer->attachment == DRI2BufferFrontLeft && 335 pDestBuffer->attachment == DRI2BufferFakeFrontLeft) { 336 ms->ctx->flush(ms->ctx, PIPE_FLUSH_SWAPBUFFERS, NULL); 337 return; 338 } 339 /* pixmap glXWaitGL */ 340 if (pDestBuffer->attachment == DRI2BufferFrontLeft && 341 pSrcBuffer->attachment == DRI2BufferFakeFrontLeft) { 342 return; 343 } else { 344 xf86DrvMsg(pScrn->scrnIndex, X_WARNING, 345 "copying between the same pixmap\n"); 346 } 347 } 348 349 gc = GetScratchGC(pDraw->depth, pScreen); 350 copy_clip = REGION_CREATE(pScreen, NULL, 0); 351 REGION_COPY(pScreen, copy_clip, pRegion); 352 (*gc->funcs->ChangeClip) (gc, CT_REGION, copy_clip, 0); 353 ValidateGC(dst_draw, gc); 354 355 /* If this is a full buffer swap, throttle on the previous one */ 356 if (ms->swapThrottling && 357 dst_priv->fence && REGION_NUM_RECTS(pRegion) == 1) { 358 BoxPtr extents = REGION_EXTENTS(pScreen, pRegion); 359 360 if (extents->x1 == 0 && extents->y1 == 0 && 361 extents->x2 == pDraw->width && extents->y2 == pDraw->height) { 362 ms->screen->fence_finish(ms->screen, dst_priv->fence, 0); 363 ms->screen->fence_reference(ms->screen, &dst_priv->fence, NULL); 364 } 365 } 366 367 /* Try to make sure the blit will be accelerated */ 368 save_accel = ms->exa->accel; 369 ms->exa->accel = TRUE; 370 371 /* In case it won't be though, make sure the GPU copy contents of the 372 * source pixmap will be used for the software fallback - presumably the 373 * client modified them before calling in here. 374 */ 375 exaMoveInPixmap(src_priv->pPixmap); 376 DamageRegionAppend(src_draw, pRegion); 377 DamageRegionProcessPending(src_draw); 378 379 if (cust && cust->winsys_context_throttle) 380 cust->winsys_context_throttle(cust, ms->ctx, THROTTLE_SWAP); 381 382 (*gc->ops->CopyArea)(src_draw, dst_draw, gc, 383 0, 0, pDraw->width, pDraw->height, 0, 0); 384 ms->exa->accel = save_accel; 385 386 FreeScratchGC(gc); 387 388 ms->ctx->flush(ms->ctx, PIPE_FLUSH_SWAPBUFFERS, 389 (pDestBuffer->attachment == DRI2BufferFrontLeft 390 && ms->swapThrottling) ? 391 &dst_priv->fence : NULL); 392 393 if (cust && cust->winsys_context_throttle) 394 cust->winsys_context_throttle(cust, ms->ctx, THROTTLE_RENDER); 395 396} 397 398Bool 399xorg_dri2_init(ScreenPtr pScreen) 400{ 401 ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; 402 modesettingPtr ms = modesettingPTR(pScrn); 403 DRI2InfoRec dri2info; 404#if DRI2INFOREC_VERSION >= 2 405 int major, minor; 406 407 if (xf86LoaderCheckSymbol("DRI2Version")) { 408 DRI2Version(&major, &minor); 409 } else { 410 /* Assume version 1.0 */ 411 major = 1; 412 minor = 0; 413 } 414#endif 415 416 dri2info.version = min(DRI2INFOREC_VERSION, 3); 417 dri2info.fd = ms->fd; 418 419 dri2info.driverName = pScrn->driverName; 420 dri2info.deviceName = "/dev/dri/card0"; /* FIXME */ 421 422#if DRI2INFOREC_VERSION >= 2 423 dri2info.CreateBuffer = dri2_create_buffer; 424 dri2info.DestroyBuffer = dri2_destroy_buffer; 425#endif 426 427 /* For X servers in the 1.6.x series there where two DRI2 version. 428 * This allows us to build one binary that works on both servers. 429 */ 430#if DRI2INFOREC_VERSION == 2 431 if (minor == 0) { 432 set_format_in_do_create_buffer = FALSE; 433 dri2info.CreateBuffers = dri2_create_buffers; 434 dri2info.DestroyBuffers = dri2_destroy_buffers; 435 } else 436 set_format_in_do_create_buffer = FALSE; 437#endif 438 439 /* For version 1 set these unconditionaly. */ 440#if DRI2INFOREC_VERSION == 1 441 dri2info.CreateBuffers = dri2_create_buffers; 442 dri2info.DestroyBuffers = dri2_destroy_buffers; 443#endif 444 dri2info.CopyRegion = dri2_copy_region; 445 dri2info.Wait = NULL; 446 447 ms->d_depth_bits_last = 448 ms->screen->is_format_supported(ms->screen, PIPE_FORMAT_Z24X8_UNORM, 449 PIPE_TEXTURE_2D, 450 0, 451 PIPE_BIND_DEPTH_STENCIL, 0); 452 ms->ds_depth_bits_last = 453 ms->screen->is_format_supported(ms->screen, PIPE_FORMAT_Z24_UNORM_S8_USCALED, 454 PIPE_TEXTURE_2D, 455 0, 456 PIPE_BIND_DEPTH_STENCIL, 0); 457 458 return DRI2ScreenInit(pScreen, &dri2info); 459} 460 461void 462xorg_dri2_close(ScreenPtr pScreen) 463{ 464 DRI2CloseScreen(pScreen); 465} 466 467/* vim: set sw=4 ts=8 sts=4: */ 468