Home | History | Annotate | Download | only in xorg
      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 (at) tungstengraphics.com>
     27  * Author: Jakob Bornecrantz <wallbraker (at) 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
     53 static Bool set_format_in_do_create_buffer;
     54 #endif
     55 
     56 typedef struct {
     57     PixmapPtr pPixmap;
     58     struct pipe_resource *tex;
     59     struct pipe_fence_handle *fence;
     60 } *BufferPrivatePtr;
     61 
     62 static Bool
     63 dri2_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 
    182 static void
    183 dri2_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 
    199 static DRI2Buffer2Ptr
    200 dri2_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);
    222 fail:
    223     free(buffer);
    224     return NULL;
    225 }
    226 
    227 static void
    228 dri2_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 
    241 static DRI2BufferPtr
    242 dri2_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 
    266 fail:
    267     free(privates);
    268 fail_privates:
    269     free(buffers);
    270 fail_buffers:
    271     return NULL;
    272 }
    273 
    274 static void
    275 dri2_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 
    291 static void
    292 dri2_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 
    404 Bool
    405 xorg_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 
    467 void
    468 xorg_dri2_close(ScreenPtr pScreen)
    469 {
    470     DRI2CloseScreen(pScreen);
    471 }
    472 
    473 /* vim: set sw=4 ts=8 sts=4: */
    474