xenocara/driver/xf86-video-vmware/vmwgfx/vmwgfx_dri2.c
2014-09-27 17:39:03 +00:00

477 lines
13 KiB
C

/*
* 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 <alanh@tungstengraphics.com>
* Author: Jakob Bornecrantz <wallbraker@gmail.com>
* Author: Thomas Hellstrom <thellstrom@vmware.com>
*
*/
#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 <unistd.h>
#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: */