/* * Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sub license, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial portions * of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * * Author: Alan Hourihane * Author: Jakob Bornecrantz * */ #include "xorg-server.h" #include "xf86.h" #include "xf86_OSproc.h" #include "xorg_tracker.h" #include "xorg_exa.h" #include "dri2.h" #include "pipe/p_state.h" #include "util/u_inlines.h" #include "util/u_format.h" #include "state_tracker/drm_driver.h" /* Make all the #if cases in the code esier to read */ #ifndef DRI2INFOREC_VERSION #define DRI2INFOREC_VERSION 1 #endif #if DRI2INFOREC_VERSION == 2 static Bool set_format_in_do_create_buffer; #endif typedef struct { PixmapPtr pPixmap; struct pipe_resource *tex; struct pipe_fence_handle *fence; } *BufferPrivatePtr; static Bool dri2_do_create_buffer(DrawablePtr pDraw, DRI2BufferPtr buffer, unsigned int format) { struct pipe_resource *tex = NULL; ScreenPtr pScreen = pDraw->pScreen; ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); modesettingPtr ms = modesettingPTR(pScrn); struct exa_pixmap_priv *exa_priv; BufferPrivatePtr private = buffer->driverPrivate; PixmapPtr pPixmap; struct winsys_handle whandle; if (pDraw->type == DRAWABLE_PIXMAP) pPixmap = (PixmapPtr) pDraw; else pPixmap = (*pScreen->GetWindowPixmap)((WindowPtr) pDraw); exa_priv = exaGetPixmapDriverPrivate(pPixmap); switch (buffer->attachment) { default: if (buffer->attachment != DRI2BufferFakeFrontLeft || pDraw->type != DRAWABLE_PIXMAP) { private->pPixmap = (*pScreen->CreatePixmap)(pScreen, pDraw->width, pDraw->height, pDraw->depth, 0); } break; case DRI2BufferFrontLeft: break; case DRI2BufferStencil: #if DRI2INFOREC_VERSION >= 3 case DRI2BufferDepthStencil: #else /* Works on old X servers because sanity checking is for the weak */ case 9: #endif if (exa_priv->depth_stencil_tex && !util_format_is_depth_or_stencil(exa_priv->depth_stencil_tex->format)) exa_priv->depth_stencil_tex = NULL; /* Fall through */ case DRI2BufferDepth: if (exa_priv->depth_stencil_tex) pipe_resource_reference(&tex, exa_priv->depth_stencil_tex); else { struct pipe_resource template; unsigned depthBits = (format != 0) ? format : pDraw->depth; memset(&template, 0, sizeof(template)); template.target = PIPE_TEXTURE_2D; if (buffer->attachment == DRI2BufferDepth) { switch(depthBits) { case 16: template.format = PIPE_FORMAT_Z16_UNORM; break; case 32: template.format = PIPE_FORMAT_Z32_UNORM; break; default: template.format = ms->ds_depth_bits_last ? PIPE_FORMAT_Z24X8_UNORM : PIPE_FORMAT_X8Z24_UNORM; break; } } else { template.format = ms->ds_depth_bits_last ? PIPE_FORMAT_Z24_UNORM_S8_UINT : PIPE_FORMAT_S8_UINT_Z24_UNORM; } template.width0 = pDraw->width; template.height0 = pDraw->height; template.depth0 = 1; template.array_size = 1; template.last_level = 0; template.bind = PIPE_BIND_DEPTH_STENCIL | PIPE_BIND_SHARED; tex = ms->screen->resource_create(ms->screen, &template); pipe_resource_reference(&exa_priv->depth_stencil_tex, tex); } break; } if (!private->pPixmap) { private->pPixmap = pPixmap; pPixmap->refcnt++; } if (!tex) { /* First call to make sure we have a pixmap private */ exaMoveInPixmap(private->pPixmap); xorg_exa_set_shared_usage(private->pPixmap); pScreen->ModifyPixmapHeader(private->pPixmap, 0, 0, 0, 0, 0, NULL); /* Second call to make sure texture has valid contents */ exaMoveInPixmap(private->pPixmap); tex = xorg_exa_get_texture(private->pPixmap); } if (!tex) FatalError("NO TEXTURE IN DRI2\n"); memset(&whandle, 0, sizeof(whandle)); whandle.type = DRM_API_HANDLE_TYPE_SHARED; ms->screen->resource_get_handle(ms->screen, tex, &whandle); buffer->name = whandle.handle; buffer->pitch = whandle.stride; buffer->cpp = 4; buffer->driverPrivate = private; buffer->flags = 0; /* not tiled */ #if DRI2INFOREC_VERSION == 2 /* ABI forwards/backwards compatibility */ if (set_format_in_do_create_buffer) ((DRI2Buffer2Ptr)buffer)->format = 0; #elif DRI2INFOREC_VERSION >= 3 buffer->format = 0; #endif private->tex = tex; return TRUE; } static void dri2_do_destroy_buffer(DrawablePtr pDraw, DRI2BufferPtr buffer) { ScreenPtr pScreen = pDraw->pScreen; ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); modesettingPtr ms = modesettingPTR(pScrn); BufferPrivatePtr private = buffer->driverPrivate; struct exa_pixmap_priv *exa_priv = exaGetPixmapDriverPrivate(private->pPixmap); pipe_resource_reference(&private->tex, NULL); ms->screen->fence_reference(ms->screen, &private->fence, NULL); pipe_resource_reference(&exa_priv->depth_stencil_tex, NULL); (*pScreen->DestroyPixmap)(private->pPixmap); } #if DRI2INFOREC_VERSION >= 2 static DRI2Buffer2Ptr dri2_create_buffer(DrawablePtr pDraw, unsigned int attachment, unsigned int format) { DRI2Buffer2Ptr buffer; BufferPrivatePtr private; buffer = calloc(1, sizeof *buffer); if (!buffer) return NULL; private = calloc(1, sizeof *private); if (!private) { goto fail; } buffer->attachment = attachment; buffer->driverPrivate = private; /* So far it is safe to downcast a DRI2Buffer2Ptr to DRI2BufferPtr */ if (dri2_do_create_buffer(pDraw, (DRI2BufferPtr)buffer, format)) return buffer; free(private); fail: free(buffer); return NULL; } static void dri2_destroy_buffer(DrawablePtr pDraw, DRI2Buffer2Ptr buffer) { /* So far it is safe to downcast a DRI2Buffer2Ptr to DRI2BufferPtr */ dri2_do_destroy_buffer(pDraw, (DRI2BufferPtr)buffer); free(buffer->driverPrivate); free(buffer); } #endif /* DRI2INFOREC_VERSION >= 2 */ #if DRI2INFOREC_VERSION <= 2 static DRI2BufferPtr dri2_create_buffers(DrawablePtr pDraw, unsigned int *attachments, int count) { BufferPrivatePtr privates; DRI2BufferPtr buffers; int i; buffers = calloc(count, sizeof *buffers); if (!buffers) goto fail_buffers; privates = calloc(count, sizeof *privates); if (!privates) goto fail_privates; for (i = 0; i < count; i++) { buffers[i].attachment = attachments[i]; buffers[i].driverPrivate = &privates[i]; if (!dri2_do_create_buffer(pDraw, &buffers[i], 0)) goto fail; } return buffers; fail: free(privates); fail_privates: free(buffers); fail_buffers: return NULL; } static void dri2_destroy_buffers(DrawablePtr pDraw, DRI2BufferPtr buffers, int count) { int i; for (i = 0; i < count; i++) { dri2_do_destroy_buffer(pDraw, &buffers[i]); } if (buffers) { free(buffers[0].driverPrivate); free(buffers); } } #endif /* DRI2INFOREC_VERSION <= 2 */ static void dri2_copy_region(DrawablePtr pDraw, RegionPtr pRegion, DRI2BufferPtr pDestBuffer, DRI2BufferPtr pSrcBuffer) { ScreenPtr pScreen = pDraw->pScreen; ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); modesettingPtr ms = modesettingPTR(pScrn); BufferPrivatePtr dst_priv = pDestBuffer->driverPrivate; BufferPrivatePtr src_priv = pSrcBuffer->driverPrivate; DrawablePtr src_draw; DrawablePtr dst_draw; GCPtr gc; RegionPtr copy_clip; Bool save_accel; CustomizerPtr cust = ms->cust; /* * In driCreateBuffers we dewrap windows into the * backing pixmaps in order to get to the texture. * We need to use the real drawable in CopyArea * so that cliprects and offsets are correct. */ src_draw = (pSrcBuffer->attachment == DRI2BufferFrontLeft) ? pDraw : &src_priv->pPixmap->drawable; dst_draw = (pDestBuffer->attachment == DRI2BufferFrontLeft) ? pDraw : &dst_priv->pPixmap->drawable; /* * The clients implements glXWaitX with a copy front to fake and then * waiting on the server to signal its completion of it. While * glXWaitGL is a client side flush and a copy from fake to front. * This is how it is done in the DRI2 protocol, how ever depending * which type of drawables the server does things a bit differently * then what the protocol says as the fake and front are the same. * * for pixmaps glXWaitX is a server flush. * for pixmaps glXWaitGL is a client flush. * for windows glXWaitX is a copy from front to fake then a server flush. * for windows glXWaitGL is a client flush then a copy from fake to front. * * XXX in the windows case this code always flushes but that isn't a * must in the glXWaitGL case but we don't know if this is a glXWaitGL * or a glFlush/glFinish call. */ if (dst_priv->pPixmap == src_priv->pPixmap) { /* pixmap glXWaitX */ if (pSrcBuffer->attachment == DRI2BufferFrontLeft && pDestBuffer->attachment == DRI2BufferFakeFrontLeft) { ms->ctx->flush(ms->ctx, NULL); return; } /* pixmap glXWaitGL */ if (pDestBuffer->attachment == DRI2BufferFrontLeft && pSrcBuffer->attachment == DRI2BufferFakeFrontLeft) { return; } else { xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "copying between the same pixmap\n"); } } gc = GetScratchGC(pDraw->depth, pScreen); copy_clip = REGION_CREATE(pScreen, NULL, 0); REGION_COPY(pScreen, copy_clip, pRegion); (*gc->funcs->ChangeClip) (gc, CT_REGION, copy_clip, 0); ValidateGC(dst_draw, gc); /* If this is a full buffer swap, throttle on the previous one */ if (ms->swapThrottling && dst_priv->fence && REGION_NUM_RECTS(pRegion) == 1) { BoxPtr extents = REGION_EXTENTS(pScreen, pRegion); if (extents->x1 == 0 && extents->y1 == 0 && extents->x2 == pDraw->width && extents->y2 == pDraw->height) { ms->screen->fence_finish(ms->screen, dst_priv->fence, PIPE_TIMEOUT_INFINITE); ms->screen->fence_reference(ms->screen, &dst_priv->fence, NULL); } } /* Try to make sure the blit will be accelerated */ save_accel = ms->exa->accel; ms->exa->accel = TRUE; if (pSrcBuffer->attachment != DRI2BufferFrontLeft) { /* In case it won't be though, make sure the GPU copy contents of the * source pixmap will be used for the software fallback - presumably the * client modified them before calling in here. */ exaMoveInPixmap(src_priv->pPixmap); DamageRegionAppend(src_draw, pRegion); DamageRegionProcessPending(src_draw); } if (cust && cust->winsys_context_throttle) cust->winsys_context_throttle(cust, ms->ctx, THROTTLE_SWAP); (*gc->ops->CopyArea)(src_draw, dst_draw, gc, 0, 0, pDraw->width, pDraw->height, 0, 0); ms->exa->accel = save_accel; FreeScratchGC(gc); ms->ctx->flush(ms->ctx, (pDestBuffer->attachment == DRI2BufferFrontLeft && ms->swapThrottling) ? &dst_priv->fence : NULL); if (cust && cust->winsys_context_throttle) cust->winsys_context_throttle(cust, ms->ctx, THROTTLE_RENDER); } Bool xorg_dri2_init(ScreenPtr pScreen) { ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); modesettingPtr ms = modesettingPTR(pScrn); DRI2InfoRec dri2info; #if DRI2INFOREC_VERSION >= 2 int major, minor; if (xf86LoaderCheckSymbol("DRI2Version")) { DRI2Version(&major, &minor); } else { /* Assume version 1.0 */ major = 1; minor = 0; } #endif dri2info.version = min(DRI2INFOREC_VERSION, 3); dri2info.fd = ms->fd; dri2info.driverName = pScrn->driverName; dri2info.deviceName = "/dev/dri/card0"; /* FIXME */ #if DRI2INFOREC_VERSION >= 2 dri2info.CreateBuffer = dri2_create_buffer; dri2info.DestroyBuffer = dri2_destroy_buffer; #endif /* For X servers in the 1.6.x series there where two DRI2 version. * This allows us to build one binary that works on both servers. */ #if DRI2INFOREC_VERSION == 2 if (minor == 0) { set_format_in_do_create_buffer = FALSE; dri2info.CreateBuffers = dri2_create_buffers; dri2info.DestroyBuffers = dri2_destroy_buffers; } else set_format_in_do_create_buffer = FALSE; #endif /* For version 1 set these unconditionaly. */ #if DRI2INFOREC_VERSION == 1 dri2info.CreateBuffers = dri2_create_buffers; dri2info.DestroyBuffers = dri2_destroy_buffers; #endif dri2info.CopyRegion = dri2_copy_region; dri2info.Wait = NULL; ms->d_depth_bits_last = ms->screen->is_format_supported(ms->screen, PIPE_FORMAT_Z24X8_UNORM, PIPE_TEXTURE_2D, 0, PIPE_BIND_DEPTH_STENCIL); ms->ds_depth_bits_last = ms->screen->is_format_supported(ms->screen, PIPE_FORMAT_Z24_UNORM_S8_UINT, PIPE_TEXTURE_2D, 0, PIPE_BIND_DEPTH_STENCIL); return DRI2ScreenInit(pScreen, &dri2info); } void xorg_dri2_close(ScreenPtr pScreen) { DRI2CloseScreen(pScreen); } /* vim: set sw=4 ts=8 sts=4: */