/* * Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas. * Copyright 2011 VMWare, Inc. * 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 * Author: Thomas Hellstrom * */ #include "xorg-server.h" #include "xf86.h" #include "xf86_OSproc.h" #include "vmwgfx_driver.h" #include "../saa/saa.h" #include "dri2.h" #include "gcstruct.h" #include "gc.h" #include "vmwgfx_saa.h" #include "wsbm_util.h" #include #include "vmwgfx_hosted.h" #define VMWGFX_FD_PATH_LEN 80 typedef struct { int refcount; PixmapPtr pPixmap; struct xa_surface *srf; unsigned int dri2_depth; } *BufferPrivatePtr; /* * Attempt to guess what the dri state tracker is up to. * Currently it sends only bpp as format. */ static unsigned int vmwgfx_color_format_to_depth(unsigned int format) { return format; } static unsigned int vmwgfx_zs_format_to_depth(unsigned int format) { if (format == 24) return 32; return format; } static unsigned int vmwgfx_z_format_to_depth(unsigned int format) { return format; } static Bool dri2_do_create_buffer(DrawablePtr pDraw, DRI2Buffer2Ptr buffer, unsigned int format) { ScreenPtr pScreen = pDraw->pScreen; ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); modesettingPtr ms = modesettingPTR(pScrn); BufferPrivatePtr private = buffer->driverPrivate; PixmapPtr pPixmap; struct vmwgfx_saa_pixmap *vpix; struct xa_surface *srf = NULL; unsigned int depth; if (pDraw->type == DRAWABLE_PIXMAP) pPixmap = (PixmapPtr) pDraw; else pPixmap = (*pScreen->GetWindowPixmap)((WindowPtr) pDraw); vpix = vmwgfx_saa_pixmap(pPixmap); private->refcount = 0; switch (buffer->attachment) { default: depth = (format) ? vmwgfx_color_format_to_depth(format) : pDraw->depth; if (buffer->attachment != DRI2BufferFakeFrontLeft || &pPixmap->drawable != pDraw) { pPixmap = (*pScreen->CreatePixmap)(pScreen, pDraw->width, pDraw->height, depth, 0); if (pPixmap == NullPixmap) return FALSE; private->pPixmap = pPixmap; private->dri2_depth = depth; vpix = vmwgfx_saa_pixmap(pPixmap); } break; case DRI2BufferFrontLeft: if (&pPixmap->drawable == pDraw) break; buffer->name = 0; buffer->pitch = 0; buffer->cpp = pDraw->bitsPerPixel / 8; buffer->driverPrivate = private; buffer->flags = 0; /* not tiled */ buffer->format = pDraw->bitsPerPixel; if (!private->pPixmap) { private->dri2_depth = 0; private->pPixmap = pPixmap; pPixmap->refcnt++; } return TRUE; case DRI2BufferStencil: case DRI2BufferDepthStencil: if (!pScrn->vtSema) return FALSE; depth = (format) ? vmwgfx_zs_format_to_depth(format) : 32; /* * The SVGA device uses the zs ordering. */ srf = xa_surface_create(ms->xat, pDraw->width, pDraw->height, depth, xa_type_zs, xa_format_unknown, XA_FLAG_SHARED ); if (!srf) return FALSE; private->dri2_depth = depth; break; case DRI2BufferDepth: if (!pScrn->vtSema) return FALSE; depth = (format) ? vmwgfx_z_format_to_depth(format) : pDraw->bitsPerPixel; if (depth == 24) srf = xa_surface_create(ms->xat, pDraw->width, pDraw->height, depth, xa_type_zs, xa_format_unknown, XA_FLAG_SHARED ); else srf = xa_surface_create(ms->xat, pDraw->width, pDraw->height, depth, xa_type_z, xa_format_unknown, XA_FLAG_SHARED); if (!srf) return FALSE; private->dri2_depth = depth; break; } if (!private->pPixmap) { private->pPixmap = pPixmap; pPixmap->refcnt++; } if (!srf) { depth = (format) ? vmwgfx_color_format_to_depth(format) : pDraw->depth; if (!vmwgfx_hw_dri2_validate(pPixmap, depth)) return FALSE; srf = vpix->hw; private->refcount++; private->dri2_depth = depth; /* * Compiz workaround. See vmwgfx_dirty(); */ if (buffer->attachment == DRI2BufferFrontLeft || buffer->attachment == DRI2BufferFakeFrontLeft) vpix->hw_is_dri2_fronts++; } private->srf = srf; if (_xa_surface_handle(srf, &buffer->name, &buffer->pitch) != 0) return FALSE; buffer->cpp = xa_format_depth(xa_surface_format(srf)) / 8; buffer->driverPrivate = private; buffer->flags = 0; /* not tiled */ buffer->format = format; private->refcount++; return TRUE; } static void dri2_do_destroy_buffer(DrawablePtr pDraw, DRI2BufferPtr buffer) { BufferPrivatePtr private = buffer->driverPrivate; struct xa_surface *srf = private->srf; ScreenPtr pScreen = pDraw->pScreen; struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(private->pPixmap); if (--private->refcount == 0 && srf) { xa_surface_destroy(srf); } /* * Compiz workaround. See vmwgfx_dirty(); */ if ((buffer->attachment == DRI2BufferFrontLeft || buffer->attachment == DRI2BufferFakeFrontLeft) && private->refcount == 1 && --vpix->hw_is_dri2_fronts == 0) WSBMLISTDELINIT(&vpix->sync_x_head); private->srf = NULL; pScreen->DestroyPixmap(private->pPixmap); } 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; if (dri2_do_create_buffer(pDraw, 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); } static void dri2_copy_region(DrawablePtr pDraw, RegionPtr pRegion, DRI2Buffer2Ptr pDestBuffer, DRI2Buffer2Ptr pSrcBuffer) { ScreenPtr pScreen = pDraw->pScreen; BufferPrivatePtr dst_priv = pDestBuffer->driverPrivate; BufferPrivatePtr src_priv = pSrcBuffer->driverPrivate; DrawablePtr src_draw; DrawablePtr dst_draw; RegionPtr myClip; GCPtr gc; ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); /* * This is a fragile protection against HW operations when not master. * Needs to be blocked higher up in the dri2 code. */ if (!pScrn->vtSema) return; /* * 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) { if (!vmwgfx_hw_dri2_validate(dst_priv->pPixmap, dst_priv->dri2_depth)) return; } /* pixmap glXWaitGL */ if (pDestBuffer->attachment == DRI2BufferFrontLeft && pSrcBuffer->attachment == DRI2BufferFakeFrontLeft) { return; } else { vmwgfx_flush_dri2(pScreen); return; } } gc = GetScratchGC(pDraw->depth, pScreen); myClip = REGION_CREATE(pScreen, REGION_RECTS(pRegion), REGION_NUM_RECTS(pRegion)); (*gc->funcs->ChangeClip) (gc, CT_REGION, myClip, 0); ValidateGC(dst_draw, gc); /* * Damage the src drawable in order for damageCopyArea to pick up * that something changed. */ DamageRegionAppend(src_draw, pRegion); if (pSrcBuffer->attachment != DRI2BufferFrontLeft) saa_drawable_dirty(src_draw, TRUE, pRegion); DamageRegionProcessPending(src_draw); /* * Call CopyArea. This usually means a call to damageCopyArea that * is wrapping saa_copy_area. The damageCopyArea function will make * sure the destination drawable is appropriately damaged. */ (*gc->ops->CopyArea)(src_draw, dst_draw, gc, 0, 0, pDraw->width, pDraw->height, 0, 0); /* * FreeScratchGC will free myClip as well. */ myClip = NULL; FreeScratchGC(gc); } #if (DRI2INFOREC_VERSION >= 8 && DRI2INFOREC_VERSION < 10) static int vmw_dri_auth_magic2(ScreenPtr pScreen, uint32_t magic) { ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); modesettingPtr ms = modesettingPTR(pScrn); return vmwgfx_hosted_dri_auth(ms->hdriver, ms->hosted, NULL, magic); } #endif #if (DRI2INFOREC_VERSION >= 10) static int vmw_dri_auth_magic3(ClientPtr client, ScreenPtr pScreen, uint32_t magic) { ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); modesettingPtr ms = modesettingPTR(pScrn); return vmwgfx_hosted_dri_auth(ms->hdriver, ms->hosted, client, magic); } #endif Bool xorg_dri2_init(ScreenPtr pScreen) { ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); modesettingPtr ms = modesettingPTR(pScrn); DRI2InfoRec dri2info; int major, minor; char fdPath[VMWGFX_FD_PATH_LEN]; ssize_t numChar; memset(&dri2info, 0, sizeof(dri2info)); if (xf86LoaderCheckSymbol("DRI2Version")) { DRI2Version(&major, &minor); } else { /* Assume version 1.0 */ major = 1; minor = 0; } dri2info.version = min(DRI2INFOREC_VERSION, 3); dri2info.fd = ms->fd; dri2info.driverName = "vmwgfx"; /* * This way of obtaining the DRM device name is a bit * os-specific. It would be better to obtain it from * drmOpen. Currently this works only for Linux. */ memset(fdPath, 0, VMWGFX_FD_PATH_LEN); snprintf(fdPath, VMWGFX_FD_PATH_LEN - 1, "/proc/self/fd/%d", ms->fd); numChar = readlink(fdPath, ms->dri2_device_name, VMWGFX_DRI_DEVICE_LEN); if (numChar <= 0 || numChar >= VMWGFX_DRI_DEVICE_LEN) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Could not find the drm device name. Disabling dri2.\n"); return FALSE; } ms->dri2_device_name[numChar] = 0; dri2info.deviceName = ms->dri2_device_name; xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Path of drm device is \"%s\".\n", ms->dri2_device_name); dri2info.CreateBuffer = dri2_create_buffer; dri2info.DestroyBuffer = dri2_destroy_buffer; dri2info.CopyRegion = dri2_copy_region; dri2info.Wait = NULL; #if (DRI2INFOREC_VERSION >= 8 && DRI2INFOREC_VERSION < 10) if (vmwgfx_is_hosted(ms->hdriver)) { dri2info.version = 8; dri2info.AuthMagic2 = vmw_dri_auth_magic2; } #endif #if (DRI2INFOREC_VERSION >= 10) if (vmwgfx_is_hosted(ms->hdriver)) { dri2info.version = 10; dri2info.AuthMagic3 = vmw_dri_auth_magic3; } #endif return DRI2ScreenInit(pScreen, &dri2info); } void xorg_dri2_close(ScreenPtr pScreen) { DRI2CloseScreen(pScreen); } /* vim: set sw=4 ts=8 sts=4: */