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