xenocara/driver/xf86-video-tdfx/src/tdfx_video.c
2012-09-08 16:18:20 +00:00

1354 lines
38 KiB
C

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "xf86.h"
#include "tdfx.h"
#include "dixstruct.h"
#include <X11/extensions/Xv.h>
#include "fourcc.h"
static Atom xvColorKey, xvFilterQuality;
/* These should move into tdfxdefs.h with better names */
#define YUV_Y_BASE 0xC00000
#define YUV_U_BASE 0xD00000
#define YUV_V_BASE 0xE00000
#define SST_2D_FORMAT_YUYV 0x8
#define SST_2D_FORMAT_UYVY 0x9
#define YUVBASEADDR 0x80100
#define YUVSTRIDE 0x80104
#define VIDPROCCFGMASK 0xa2e3eb6c
#define OFF_DELAY 250 /* milliseconds */
#define FREE_DELAY 15000
#define OFF_TIMER 0x01
#define FREE_TIMER 0x02
#define CLIENT_VIDEO_ON 0x04
#define TIMER_MASK (OFF_TIMER | FREE_TIMER)
#define TDFX_MAX_OVERLAY_PORTS 1
#define TDFX_MAX_TEXTURE_PORTS 32
#define GET_PORT_PRIVATE(pScrn) \
(TDFXPortPrivPtr)((TDFXPTR(pScrn))->overlayAdaptor->pPortPrivates[0].ptr)
/* Needed for attribute atoms */
#define MAKE_ATOM(a) MakeAtom(a, sizeof(a) - 1, TRUE)
/*
* PROTOTYPES
*/
static FBAreaPtr TDFXAllocateMemoryArea (ScrnInfoPtr pScrn, FBAreaPtr area, int width, int height);
static FBLinearPtr TDFXAllocateMemoryLinear (ScrnInfoPtr pScrn, FBLinearPtr linear, int size);
static void TDFXVideoTimerCallback(ScrnInfoPtr pScrn, Time time);
static XF86VideoAdaptorPtr TDFXSetupImageVideoTexture(ScreenPtr);
static int TDFXSetPortAttributeTexture(ScrnInfoPtr, Atom, INT32, pointer);
static int TDFXGetPortAttributeTexture(ScrnInfoPtr, Atom ,INT32 *, pointer);
static int TDFXPutImageTexture(ScrnInfoPtr, short, short, short, short, short, short, short, short, int, unsigned char*, short, short, Bool, RegionPtr, pointer, DrawablePtr);
static void TDFXStopVideoTexture(ScrnInfoPtr, pointer, Bool);
static XF86VideoAdaptorPtr TDFXSetupImageVideoOverlay(ScreenPtr);
static int TDFXSetPortAttributeOverlay(ScrnInfoPtr, Atom, INT32, pointer);
static int TDFXGetPortAttributeOverlay(ScrnInfoPtr, Atom ,INT32 *, pointer);
static int TDFXPutImageOverlay(ScrnInfoPtr, short, short, short, short, short, short, short, short, int, unsigned char*, short, short, Bool, RegionPtr, pointer, DrawablePtr);
static void TDFXStopVideoOverlay(ScrnInfoPtr, pointer, Bool);
static void TDFXResetVideoOverlay(ScrnInfoPtr);
static void TDFXQueryBestSize(ScrnInfoPtr, Bool, short, short, short, short, unsigned int *, unsigned int *, pointer);
static int TDFXQueryImageAttributes(ScrnInfoPtr, int, unsigned short *, unsigned short *, int *, int *);
static void TDFXInitOffscreenImages(ScreenPtr);
/*
* ADAPTOR INFORMATION
*/
static XF86VideoEncodingRec OverlayEncoding[] =
{
{ 0, "XV_IMAGE", 2048, 2048, {1, 1} }
};
static XF86VideoEncodingRec TextureEncoding[] =
{
{ 0, "XV_IMAGE", 1024, 1024, {1, 1} }
};
static XF86VideoFormatRec OverlayFormats[] =
{
{8, TrueColor}, {8, DirectColor}, {8, PseudoColor},
{8, GrayScale}, {8, StaticGray}, {8, StaticColor},
{15, TrueColor}, {16, TrueColor}, {24, TrueColor},
{15, DirectColor}, {16, DirectColor}, {24, DirectColor}
};
static XF86VideoFormatRec TextureFormats[] =
{
{15, TrueColor}, {16, TrueColor}, {24, TrueColor}
};
static XF86AttributeRec OverlayAttributes[] =
{
{XvSettable | XvGettable, 0, (1 << 24) - 1, "XV_COLORKEY"},
{XvSettable | XvGettable, 0, 1, "XV_FILTER_QUALITY"}
};
static XF86AttributeRec TextureAttributes[] =
{
{XvSettable | XvGettable, 0, (1 << 24) - 1, "XV_COLORKEY"},
{XvSettable | XvGettable, 0, 1, "XV_FILTER_QUALITY"}
};
static XF86ImageRec OverlayImages[] =
{
XVIMAGE_YUY2, XVIMAGE_UYVY, XVIMAGE_YV12, XVIMAGE_I420
};
static XF86ImageRec TextureImages[] =
{
XVIMAGE_YV12, XVIMAGE_I420
};
/*
* COMMON SETUP FUNCTIONS
*/
void TDFXInitVideo(ScreenPtr pScreen)
{
ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
XF86VideoAdaptorPtr *adaptors, *newAdaptors = NULL;
XF86VideoAdaptorPtr newAdaptor = NULL;
TDFXPtr pTDFX = TDFXPTR(pScrn);
int num_adaptors;
/* The hardware can't convert YUV->8 bit color */
if(pTDFX->cpp == 1)
return;
if (!pTDFX->TextureXvideo) {
/* Offscreen support for Overlay only */
TDFXInitOffscreenImages(pScreen);
/* Overlay adaptor */
newAdaptor = TDFXSetupImageVideoOverlay(pScreen);
} else {
/* Texture adaptor */
newAdaptor = TDFXSetupImageVideoTexture(pScreen);
}
num_adaptors = xf86XVListGenericAdaptors(pScrn, &adaptors);
if(newAdaptor) {
if (!num_adaptors) {
num_adaptors = 1;
adaptors = &newAdaptor;
} else {
newAdaptors =
malloc((num_adaptors + 1) * sizeof(XF86VideoAdaptorPtr*));
if(newAdaptors) {
memcpy(newAdaptors, adaptors, num_adaptors *
sizeof(XF86VideoAdaptorPtr));
newAdaptors[num_adaptors] = newAdaptor;
adaptors = newAdaptors;
num_adaptors++;
}
}
}
if(num_adaptors)
xf86XVScreenInit(pScreen, adaptors, num_adaptors);
if(newAdaptors)
free(newAdaptors);
}
void TDFXCloseVideo (ScreenPtr pScreen)
{
}
static XF86VideoAdaptorPtr
TDFXAllocAdaptor(ScrnInfoPtr pScrn, int numberPorts)
{
XF86VideoAdaptorPtr adapt;
TDFXPtr pTDFX = TDFXPTR(pScrn);
TDFXPortPrivPtr pPriv;
if(!(adapt = xf86XVAllocateVideoAdaptorRec(pScrn)))
return NULL;
if(!(pPriv = calloc(1, sizeof(TDFXPortPrivRec) + (numberPorts * sizeof(DevUnion)))))
{
free(adapt);
return NULL;
}
adapt->pPortPrivates = (DevUnion*)(&pPriv[1]);
adapt->pPortPrivates[0].ptr = (pointer)pPriv;
xvColorKey = MAKE_ATOM("XV_COLORKEY");
xvFilterQuality = MAKE_ATOM("XV_FILTER_QUALITY");
pPriv->colorKey = pTDFX->videoKey;
pPriv->videoStatus = 0;
pPriv->filterQuality = 1;
return adapt;
}
static XF86VideoAdaptorPtr
TDFXSetupImageVideoOverlay(ScreenPtr pScreen)
{
ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
TDFXPtr pTDFX = TDFXPTR(pScrn);
TDFXPortPrivPtr pPriv;
XF86VideoAdaptorPtr adapt;
if(!(adapt = TDFXAllocAdaptor(pScrn, TDFX_MAX_OVERLAY_PORTS)))
return NULL;
adapt->type = XvWindowMask | XvInputMask | XvImageMask;
adapt->flags = VIDEO_OVERLAID_IMAGES | VIDEO_CLIP_TO_VIEWPORT;
adapt->name = "3dfx Video Overlay";
adapt->nPorts = TDFX_MAX_OVERLAY_PORTS;
adapt->nEncodings = sizeof(OverlayEncoding) / sizeof(XF86VideoEncodingRec);
adapt->pEncodings = OverlayEncoding;
adapt->nFormats = sizeof(OverlayFormats) / sizeof(XF86VideoFormatRec);
adapt->pFormats = OverlayFormats;
adapt->nAttributes = sizeof(OverlayAttributes) / sizeof(XF86AttributeRec);
adapt->pAttributes = OverlayAttributes;
adapt->nImages = sizeof(OverlayImages) / sizeof(XF86ImageRec);
adapt->pImages = OverlayImages;
adapt->PutVideo = NULL;
adapt->PutStill = NULL;
adapt->GetVideo = NULL;
adapt->GetStill = NULL;
adapt->StopVideo = TDFXStopVideoOverlay;
adapt->SetPortAttribute = TDFXSetPortAttributeOverlay;
adapt->GetPortAttribute = TDFXGetPortAttributeOverlay;
adapt->QueryBestSize = TDFXQueryBestSize;
adapt->PutImage = TDFXPutImageOverlay;
adapt->QueryImageAttributes = TDFXQueryImageAttributes;
pTDFX->overlayAdaptor = adapt;
pPriv = (TDFXPortPrivPtr)(adapt->pPortPrivates[0].ptr);
REGION_NULL(pScreen, &(pPriv->clip));
TDFXResetVideoOverlay(pScrn);
return adapt;
}
static XF86VideoAdaptorPtr
TDFXSetupImageVideoTexture(ScreenPtr pScreen)
{
ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
TDFXPtr pTDFX = TDFXPTR(pScrn);
XF86VideoAdaptorPtr adapt;
int i;
if(!(adapt = TDFXAllocAdaptor(pScrn, TDFX_MAX_TEXTURE_PORTS)))
return NULL;
adapt->type = XvWindowMask | XvInputMask | XvImageMask;
adapt->flags = VIDEO_OVERLAID_IMAGES;
adapt->name = "3dfx Video Texture";
adapt->nPorts = TDFX_MAX_TEXTURE_PORTS;
adapt->nEncodings = sizeof(TextureEncoding) / sizeof(XF86VideoEncodingRec);
adapt->pEncodings = TextureEncoding;
adapt->nFormats = sizeof(TextureFormats) / sizeof(XF86VideoFormatRec);
adapt->pFormats = TextureFormats;
adapt->nAttributes = sizeof(TextureAttributes) / sizeof(XF86AttributeRec);
adapt->pAttributes = TextureAttributes;
adapt->nImages = sizeof(TextureImages) / sizeof(XF86ImageRec);
adapt->pImages = TextureImages;
adapt->PutVideo = NULL;
adapt->PutStill = NULL;
adapt->GetVideo = NULL;
adapt->GetStill = NULL;
adapt->StopVideo = TDFXStopVideoTexture;
adapt->SetPortAttribute = TDFXSetPortAttributeTexture;
adapt->GetPortAttribute = TDFXGetPortAttributeTexture;
adapt->QueryBestSize = TDFXQueryBestSize;
adapt->PutImage = TDFXPutImageTexture;
adapt->QueryImageAttributes = TDFXQueryImageAttributes;
for(i = 0; i < TDFX_MAX_TEXTURE_PORTS; i++)
adapt->pPortPrivates[i].val = i;
pTDFX->textureAdaptor = adapt;
return adapt;
}
/*
* MISCELLANEOUS ROUTINES
*/
static int
TDFXQueryImageAttributes(
ScrnInfoPtr pScrn,
int id,
unsigned short *w, unsigned short *h,
int *pitches, int *offsets
){
int size, tmp;
if(*w > 1024) *w = 1024;
if(*h > 1024) *h = 1024;
*w = (*w + 1) & ~1;
if(offsets) offsets[0] = 0;
switch(id) {
case FOURCC_YV12:
case FOURCC_I420:
*h = (*h + 1) & ~1;
size = (*w + 3) & ~3;
if(pitches) pitches[0] = size;
size *= *h;
if(offsets) offsets[1] = size;
tmp = ((*w >> 1) + 3) & ~3;
if(pitches) pitches[1] = pitches[2] = tmp;
tmp *= (*h >> 1);
size += tmp;
if(offsets) offsets[2] = size;
size += tmp;
break;
case FOURCC_UYVY:
case FOURCC_YUY2:
default:
size = *w << 1;
if(pitches) pitches[0] = size;
size *= *h;
break;
}
return size;
}
static int
TDFXSetPortAttributeOverlay(
ScrnInfoPtr pScrn,
Atom attribute,
INT32 value,
pointer data
){
TDFXPortPrivPtr pPriv = (TDFXPortPrivPtr)data;
TDFXPtr pTDFX = TDFXPTR(pScrn);
if(attribute == xvColorKey) {
pPriv->colorKey = value;
pTDFX->writeLong(pTDFX, VIDCHROMAMIN, pPriv->colorKey);
pTDFX->writeLong(pTDFX, VIDCHROMAMAX, pPriv->colorKey);
REGION_EMPTY(pScrn->pScreen, &pPriv->clip);
} else if(attribute == xvFilterQuality) {
if((value < 0) || (value > 1))
return BadValue;
pPriv->filterQuality = value;
} else return BadMatch;
return Success;
}
static int
TDFXGetPortAttributeOverlay(
ScrnInfoPtr pScrn,
Atom attribute,
INT32 *value,
pointer data
){
TDFXPortPrivPtr pPriv = (TDFXPortPrivPtr)data;
if(attribute == xvColorKey) {
*value = pPriv->colorKey;
} else if(attribute == xvFilterQuality) {
*value = pPriv->filterQuality;
} else return BadMatch;
return Success;
}
static int
TDFXSetPortAttributeTexture(
ScrnInfoPtr pScrn,
Atom attribute,
INT32 value,
pointer data
) {
return Success;
}
static int
TDFXGetPortAttributeTexture(
ScrnInfoPtr pScrn,
Atom attribute,
INT32 *value,
pointer data
){
return Success;
}
static void
TDFXQueryBestSize(
ScrnInfoPtr pScrn,
Bool motion,
short vid_w, short vid_h,
short drw_w, short drw_h,
unsigned int *p_w, unsigned int *p_h,
pointer data
){
if(vid_w > drw_w) drw_w = vid_w;
if(vid_h > drw_h) drw_h = vid_h;
*p_w = drw_w;
*p_h = drw_h;
}
static void
TDFXCopyData(
unsigned char *src,
unsigned char *dst,
int srcPitch,
int dstPitch,
int h,
int w
){
#if X_BYTE_ORDER == X_BIG_ENDIAN
w >>= 1;
while(h--) {
int i;
for (i=0; i<w; i++)
((unsigned long *)dst)[i]=BE_WSWAP32(((unsigned long *)src)[i]);
src += srcPitch;
dst += dstPitch;
}
#else
w <<= 1;
while(h--) {
memcpy(dst, src, w);
src += srcPitch;
dst += dstPitch;
}
#endif
}
static void
TDFXCopyMungedData(
unsigned char *src1,
unsigned char *src2,
unsigned char *src3,
unsigned char *dst1,
int srcPitch,
int srcPitch2,
int dstPitch,
int h,
int w
){
CARD32 *dst;
CARD8 *s1, *s2, *s3;
int i, j;
w >>= 1;
for(j = 0; j < h; j++) {
dst = (CARD32*)dst1;
s1 = src1; s2 = src2; s3 = src3;
i = w;
while(i > 4) {
dst[0] = BE_WSWAP32(s1[0] | (s1[1] << 16) | (s3[0] << 8) |
(s2[0] << 24));
dst[1] = BE_WSWAP32(s1[2] | (s1[3] << 16) | (s3[1] << 8) |
(s2[1] << 24));
dst[2] = BE_WSWAP32(s1[4] | (s1[5] << 16) | (s3[2] << 8) |
(s2[2] << 24));
dst[3] = BE_WSWAP32(s1[6] | (s1[7] << 16) | (s3[3] << 8) |
(s2[3] << 24));
dst += 4; s2 += 4; s3 += 4; s1 += 8;
i -= 4;
}
while(i--) {
dst[0] = BE_WSWAP32(s1[0] | (s1[1] << 16) | (s3[0] << 8) |
(s2[0] << 24));
dst++; s2++; s3++;
s1 += 2;
}
dst1 += dstPitch;
src1 += srcPitch;
if(j & 1) {
src2 += srcPitch2;
src3 += srcPitch2;
}
}
}
/*
* TEXTURE DRAWING FUNCTIONS
*/
static void
TDFXStopVideoTexture(ScrnInfoPtr pScrn, pointer data, Bool cleanup)
{
TDFXPtr pTDFX = TDFXPTR(pScrn);
if (cleanup) {
if(pTDFX->textureBuffer) {
xf86FreeOffscreenArea(pTDFX->textureBuffer);
pTDFX->textureBuffer = NULL;
}
}
}
static void
TDFXScreenToScreenYUVStretchBlit (ScrnInfoPtr pScrn,
short src_x1, short src_y1,
short src_x2, short src_y2,
short dst_x1, short dst_y1,
short dst_x2, short dst_y2)
{
TDFXPtr pTDFX = TDFXPTR(pScrn);
/* reformulate the paramaters the way the hardware wants them */
INT32 src_x = src_x1 & 0x1FFF;
INT32 src_y = src_y1 & 0x1FFF;
INT32 dst_x = dst_x1 & 0x1FFF;
INT32 dst_y = dst_y1 & 0x1FFF;
INT32 src_w = (src_x2 - src_x1) & 0x1FFF;
INT32 src_h = (src_y2 - src_y1) & 0x1FFF;
INT32 dst_w = (dst_x2 - dst_x1) & 0x1FFF;
INT32 dst_h = (dst_y2 - dst_y1) & 0x1FFF;
/* Setup for blit src and dest */
TDFXMakeRoom(pTDFX, 4);
DECLARE(SSTCP_DSTSIZE|SSTCP_SRCSIZE|SSTCP_DSTXY|SSTCP_COMMAND/*|SSTCP_COMMANDEXTRA*/);
/* TDFXWriteLong(pTDFX, SST_2D_COMMANDEXTRA, SST_COMMANDEXTRA_VSYNC);*/
TDFXWriteLong(pTDFX, SST_2D_SRCSIZE, src_w | (src_h<<16));
TDFXWriteLong(pTDFX, SST_2D_DSTSIZE, dst_w | (dst_h<<16));
TDFXWriteLong(pTDFX, SST_2D_DSTXY, dst_x | (dst_y<<16));
TDFXWriteLong(pTDFX, SST_2D_COMMAND, SST_2D_SCRNTOSCRNSTRETCH | 0xCC000000);
/* Write to the launch area to start the blit */
TDFXMakeRoom(pTDFX, 1);
DECLARE_LAUNCH(1, 0);
TDFXWriteLong(pTDFX, SST_2D_LAUNCH, (src_x<<1) | (src_y<<16));
/* Wait for it to happen */
TDFXSendNOPFifo2D(pScrn);
}
static void
YUVPlanarToPacked (ScrnInfoPtr pScrn,
short src_x, short src_y,
short src_h, short src_w,
int id, unsigned char *buf,
short width, short height,
FBAreaPtr fbarea)
{
TDFXPtr pTDFX = TDFXPTR(pScrn);
unsigned char *psrc, *pdst;
int count;
int baseaddr;
INT32 yuvBaseAddr, yuvStride;
/* Save these registers so I can restore them when we are done. */
yuvBaseAddr = TDFXReadLongMMIO(pTDFX, YUVBASEADDR);
yuvStride = TDFXReadLongMMIO(pTDFX, YUVSTRIDE);
/* Set yuvBaseAddress and yuvStride. */
baseaddr = pTDFX->fbOffset + pTDFX->cpp * fbarea->box.x1 + pTDFX->stride * fbarea->box.y1;
TDFXWriteLongMMIO(pTDFX, YUVSTRIDE, pTDFX->stride);
TDFXWriteLongMMIO(pTDFX, YUVBASEADDR, baseaddr);
/* Copy Y plane (twice as much Y as U or V) */
psrc = buf;
psrc += (src_x & ~0x1) + src_y * width;
pdst = pTDFX->MMIOBase[0] + YUV_Y_BASE;
TDFXCopyData(psrc, pdst, width, 1024, src_h, src_w + (src_x & 0x1));
/* Copy V plane */
psrc = buf + width * height;
psrc += (src_x >> 1) + (src_y >> 1) * (width >> 1);
pdst = pTDFX->MMIOBase[0] + YUV_V_BASE;
TDFXCopyData(psrc, pdst, width >> 1, 1024, src_h >> 1, src_w >> 1);
/* Copy U plane */
psrc = buf + width * height + (width >> 1) * (height >> 1);
psrc += (src_x >> 1) + (src_y >> 1) * (width >> 1);
pdst = pTDFX->MMIOBase[0] + YUV_U_BASE;
TDFXCopyData(psrc, pdst, width >> 1, 1024, src_h >> 1, src_w >> 1);
/* IDLE until the copy finished, timeout for safety */
for (count = 0; count < 1000; count++)
if (!((TDFXReadLongMMIO(pTDFX, STATUS) & SST_BUSY)))
break;
/* Restore trashed registers */
TDFXWriteLongMMIO(pTDFX, YUVBASEADDR, yuvBaseAddr);
TDFXWriteLongMMIO(pTDFX, YUVSTRIDE, yuvStride);
/* Wait for it to happen */
TDFXSendNOPFifo2D(pScrn);
}
static int
TDFXPutImageTexture(
ScrnInfoPtr pScrn,
short src_x, short src_y,
short drw_x, short drw_y,
short src_w, short src_h,
short drw_w, short drw_h,
int id, unsigned char* buf,
short width, short height,
Bool sync,
RegionPtr clipBoxes, pointer data,
DrawablePtr pDraw
)
{
TDFXPtr pTDFX = TDFXPTR(pScrn);
BoxPtr pbox;
int nbox;
int format;
TDFXTRACE("TDFXPutImageTexture(src_x=%d, src_y=%d, drw_x=%d, drw_y=%d, .. sync=%d\n",
src_x, src_y, drw_x, drw_y, sync);
/* Check the source format */
if (id == FOURCC_YV12) format = SST_2D_FORMAT_YUYV;
else if (id == FOURCC_UYVY) format = SST_2D_FORMAT_UYVY;
else return BadAlloc;
/* Get a buffer to store the packed YUV data */
if (!(pTDFX->textureBuffer = TDFXAllocateMemoryArea(pScrn, pTDFX->textureBuffer, src_w, src_h)))
return BadAlloc;
/* Pack the YUV data in offscreen memory using YUV framebuffer (0x[CDE]0000) */
YUVPlanarToPacked (pScrn, src_x, src_y, src_h, src_w,
id, buf, width, height,
pTDFX->textureBuffer);
/* Setup source and destination pixel formats (yuv -> rgb) */
TDFXMakeRoom(pTDFX, 2);
DECLARE(SSTCP_SRCFORMAT|SSTCP_DSTFORMAT);
TDFXWriteLong(pTDFX, SST_2D_DSTFORMAT, pTDFX->stride|((pTDFX->cpp+1)<<16));
TDFXWriteLong(pTDFX, SST_2D_SRCFORMAT, pTDFX->stride|((format)<<16));
/* Blit packed YUV data from offscreen memory, respecting clips */
#define SRC_X1 (pTDFX->textureBuffer->box.x1)
#define SRC_Y1 (pTDFX->textureBuffer->box.y1)
#define SCALEX(dx) ((int)(((dx) * src_w) / drw_w))
#define SCALEY(dy) ((int)(((dy) * src_h) / drw_h))
for (nbox = REGION_NUM_RECTS(clipBoxes),
pbox = REGION_RECTS(clipBoxes); nbox > 0; nbox--, pbox++)
{
TDFXScreenToScreenYUVStretchBlit (pScrn,
SRC_X1 + SCALEX(pbox->x1 - drw_x),
SRC_Y1 + SCALEY(pbox->y1 - drw_y),
SRC_X1 + SCALEX(pbox->x2 - drw_x),
SRC_Y1 + SCALEY(pbox->y2 - drw_y),
pbox->x1, pbox->y1,
pbox->x2, pbox->y2);
}
/* Restore the WAX registers we trashed */
TDFXMakeRoom(pTDFX, 2);
DECLARE(SSTCP_SRCFORMAT|SSTCP_DSTFORMAT);
TDFXWriteLong(pTDFX, SST_2D_DSTFORMAT, pTDFX->sst2DDstFmtShadow);
TDFXWriteLong(pTDFX, SST_2D_SRCFORMAT, pTDFX->sst2DSrcFmtShadow);
/* Wait for it to happen */
TDFXSendNOPFifo2D(pScrn);
return Success;
}
/*
* OVERLAY DRAWING FUNCTIONS
*/
static void
TDFXResetVideoOverlay(ScrnInfoPtr pScrn)
{
TDFXPtr pTDFX = TDFXPTR(pScrn);
TDFXPortPrivPtr pPriv = pTDFX->overlayAdaptor->pPortPrivates[0].ptr;
/* reset the video */
pTDFX->ModeReg.vidcfg &= ~VIDPROCCFGMASK;
pTDFX->writeLong(pTDFX, VIDPROCCFG, pTDFX->ModeReg.vidcfg);
pTDFX->writeLong(pTDFX, RGBMAXDELTA, 0x0080808);
pTDFX->writeLong(pTDFX, VIDCHROMAMIN, pPriv->colorKey);
pTDFX->writeLong(pTDFX, VIDCHROMAMAX, pPriv->colorKey);
}
static void
TDFXStopVideoOverlay(ScrnInfoPtr pScrn, pointer data, Bool cleanup)
{
TDFXPtr pTDFX = TDFXPTR(pScrn);
TDFXPortPrivPtr pPriv = (TDFXPortPrivPtr)data;
REGION_EMPTY(pScrn->pScreen, &pPriv->clip);
if(cleanup) {
if(pPriv->videoStatus & CLIENT_VIDEO_ON) {
pTDFX->ModeReg.vidcfg &= ~VIDPROCCFGMASK;
pTDFX->writeLong(pTDFX, VIDPROCCFG, pTDFX->ModeReg.vidcfg);
}
if(pTDFX->overlayBuffer) {
xf86FreeOffscreenLinear(pTDFX->overlayBuffer);
pTDFX->overlayBuffer = NULL;
}
if(pTDFX->overlayBuffer2) {
xf86FreeOffscreenLinear(pTDFX->overlayBuffer2);
pTDFX->overlayBuffer2 = NULL;
}
pPriv->videoStatus = 0;
} else {
if(pPriv->videoStatus & CLIENT_VIDEO_ON) {
pPriv->videoStatus |= OFF_TIMER;
pPriv->offTime = currentTime.milliseconds + OFF_DELAY;
}
}
}
/* * * * * *
Decoder...
VIDPROCCFG: 0x5c: Video Processor Configuration register
#define VIDPROCCFGMASK 0xa2e3eb6c
3 2 1 0
10987654321098765432109876543210
10100010111000111110101101101100
The "1" bits are the bits cleared to 0 in pTDFX->ModeReg.vidcfg
Then we or in 0x320:
11
109876543210
320 = 001100100000
bit 11=0: Do not bypass clut (colour lookup) for overlay
bit 10=0: Do not bypass clut for desktop
bit 9=1: use video-in buffer address as overlay start
address (auto-flipping)
bit 8=1: fetch overlay surface
bit 7=0: do not fetch the desktop surface (?)
bit 6=0: chromakey not inverted
bit 5=1: chromakey enabled
bit 4=0: half-mode disabled
bit 3=0: not interlaced (interlace doesn't work on Avenger)
bit 2=0: overlay stereo disabled
bit 1=0: Windows cursor mode
bit 0=0: Video processor off, VGA mode on
SST_VIDEO_2X_MODE_EN: bit26: means 2 pixels per screen clock mode is on
VIDOVERLAYSTARTCOORDS: x&y coords of overlay on the screen
VIDOVERLAYENDSCREENCOORDS: x&y coorder of bot-right of overlay on the screen
VIDOVERLAYDUDX: step size in source per hoz step in screen space (x-mag)
VIDOVERLAYDUDXOFFSETSRCWIDTH:
VIDOVERLAYDVDY: step size in sourcxe per vertical step in screen (y-mag)
VIDOVERLAYDVDYOFFSET: initial offset of DVDY
VIDDESKTOPOVERLAYSTRIDE: desktop surface stride
SST_3D_LEFTOVERLAYBUF: starting physical address of the overlay surface buffer
VIDINADDR0: starting address of video-in buffer-0
[this is set, but this is for video _input_ as I understand docs...?]
* * * * * */
static void
TDFXDisplayVideoOverlay(
ScrnInfoPtr pScrn,
int id,
int offset,
short width, short height,
int pitch,
int left, int right, int top,
BoxPtr dstBox,
short src_w, short src_h,
short drw_w, short drw_h
){
TDFXPtr pTDFX = TDFXPTR(pScrn);
TDFXPortPrivPtr pPriv = pTDFX->overlayAdaptor->pPortPrivates[0].ptr;
int dudx, dvdy;
dudx = (src_w << 20) / drw_w;
/* subtract 1 to eliminate garbage on last line */
dvdy = (( src_h - 1 )<< 20) / drw_h;
offset += ((left >> 16) & ~1) << 1;
left = (left & 0x0001ffff) << 3;
pTDFX->ModeReg.vidcfg &= ~VIDPROCCFGMASK;
pTDFX->ModeReg.vidcfg |= 0x00000320;
if(drw_w != src_w) pTDFX->ModeReg.vidcfg |= (1 << 14);
if(drw_h != src_h) pTDFX->ModeReg.vidcfg |= (1 << 15);
if(id == FOURCC_UYVY) pTDFX->ModeReg.vidcfg |= (6 << 21);
else pTDFX->ModeReg.vidcfg |= (5 << 21);
if(pScrn->depth == 8) pTDFX->ModeReg.vidcfg |= (1 << 11);
/* can't do bilinear filtering when in 2X mode */
if(pPriv->filterQuality && !(pTDFX->ModeReg.vidcfg & SST_VIDEO_2X_MODE_EN))
pTDFX->ModeReg.vidcfg |= (3 << 16);
pTDFX->writeLong(pTDFX, VIDPROCCFG, pTDFX->ModeReg.vidcfg);
pTDFX->writeLong(pTDFX, VIDOVERLAYSTARTCOORDS, dstBox->x1 | (dstBox->y1 << 12));
pTDFX->writeLong(pTDFX, VIDOVERLAYENDSCREENCOORDS, (dstBox->x2 - 1) | ((dstBox->y2 - 1) << 12));
pTDFX->writeLong(pTDFX, VIDOVERLAYDUDX, dudx);
pTDFX->writeLong(pTDFX, VIDOVERLAYDUDXOFFSETSRCWIDTH, left | (src_w << 20));
pTDFX->writeLong(pTDFX, VIDOVERLAYDVDY, dvdy);
pTDFX->writeLong(pTDFX, VIDOVERLAYDVDYOFFSET, (top & 0x0000ffff) << 3);
pTDFX->ModeReg.stride &= 0x0000ffff;
pTDFX->ModeReg.stride |= pitch << 16;
pTDFX->writeLong(pTDFX, VIDDESKTOPOVERLAYSTRIDE, pTDFX->ModeReg.stride);
pTDFX->writeLong(pTDFX, SST_3D_LEFTOVERLAYBUF, offset & ~3);
pTDFX->writeLong(pTDFX, VIDINADDR0, offset & ~3);
TDFXTRACE("TDFXDisplayVideoOverlay: done, offset=0x%x\n", offset);
}
#if 0
/* * * * *
TDFXSwapVideoOverlayBuffer tries to use the Avenger SWAPBUFFER
capability to change frames without tearing.
Use this in preference to TDFXDisplayVideoOverlay where all image
parameters are the same as the previous frame - ie where only the
SST_3D_LEFTOVERLAYBUF register would have been changed.
NOTE: Work in progress - doesn't seem to sync to VSYNC, and only every
other frame gets displayed...
Seeing that the buffer swap initiated by DisplayVideoOverlay gets
synced to VSYNC anyway, just adding double-buffering to PutImageOverlay
appears to do the job. Still - I leave this code in in case we can
get it working later
-- Steve Davies 2002-10-04
-- <steve@daviesfam.org>
* * * * * */
static void
TDFXSwapVideoOverlayBuffer(
ScrnInfoPtr pScrn,
int offset,
int left
){
TDFXPtr pTDFX = TDFXPTR(pScrn);
offset += ((left >> 16) & ~1) << 1;
/* Write mew buffer address */
pTDFX->writeLong(pTDFX, SST_3D_LEFTOVERLAYBUF, offset & ~3);
/* Incremement the swap-pending counter */
pTDFX->writeLong(pTDFX, SST_3D_SWAPPENDING, 0);
/* write the swapbuffer command - triggered by (next) VSYNC */
pTDFX->writeLong(pTDFX, SST_3D_SWAPBUFFERCMD, 1);
}
#endif
static int
TDFXPutImageOverlay(
ScrnInfoPtr pScrn,
short src_x, short src_y,
short drw_x, short drw_y,
short src_w, short src_h,
short drw_w, short drw_h,
int id, unsigned char* buf,
short width, short height,
Bool Sync,
RegionPtr clipBoxes, pointer data,
DrawablePtr pDraw
){
TDFXPtr pTDFX = TDFXPTR(pScrn);
TDFXPortPrivPtr pPriv = (TDFXPortPrivPtr)data;
INT32 xa, xb, ya, yb;
unsigned char *dst_start;
int new_size, offset;
int s2offset = 0, s3offset = 0;
int srcPitch = 0, srcPitch2 = 0;
int dstPitch;
int top, left, npixels, nlines, bpp;
BoxRec dstBox;
CARD32 tmp;
TDFXTRACE("TDFXPutImageOverlay: src_x=%d, src_y=%d, drw_x=%d, drw_y=%d, src_w=%d, src_h=%d, drw_w=%d, drw_h=%d, id=%d, width=%d, height=%d, sync=%d\n",
src_x, src_y, drw_x, drw_y, src_w, src_h, drw_w, drw_h, id, width, height, Sync);
/*
* s2offset, s3offset - byte offsets into U and V plane of the
* source where copying starts. Y plane is
* done by editing "buf".
*
* offset - byte offset to the first line of the destination.
*
* dst_start - byte address to the first displayed pel.
*
*/
if(src_w > drw_w) drw_w = src_w;
if(src_h > drw_h) drw_h = src_h;
/* Clip */
xa = src_x;
xb = src_x + src_w;
ya = src_y;
yb = src_y + src_h;
dstBox.x1 = drw_x;
dstBox.x2 = drw_x + drw_w;
dstBox.y1 = drw_y;
dstBox.y2 = drw_y + drw_h;
if(!xf86XVClipVideoHelper(&dstBox, &xa, &xb, &ya, &yb,
clipBoxes, width, height))
return Success;
dstBox.x1 -= pScrn->frameX0;
dstBox.x2 -= pScrn->frameX0;
dstBox.y1 -= pScrn->frameY0;
dstBox.y2 -= pScrn->frameY0;
bpp = pScrn->bitsPerPixel >> 3;
switch(id) {
case FOURCC_YV12:
case FOURCC_I420:
dstPitch = ((width << 1) + 3) & ~3;
new_size = ((dstPitch * height) + bpp - 1) / bpp;
srcPitch = (width + 3) & ~3;
s2offset = srcPitch * height;
srcPitch2 = ((width >> 1) + 3) & ~3;
s3offset = (srcPitch2 * (height >> 1)) + s2offset;
break;
case FOURCC_UYVY:
case FOURCC_YUY2:
default:
dstPitch = ((width << 1) + 3) & ~3;
new_size = ((dstPitch * height) + bpp - 1) / bpp;
srcPitch = (width << 1);
break;
}
if(!(pTDFX->overlayBuffer = TDFXAllocateMemoryLinear(pScrn, pTDFX->overlayBuffer, new_size)))
return BadAlloc;
/* Second buffer for double-buffering (If we can't get the memory then we just don't double-buffer) */
if (!(pTDFX->overlayBuffer2 = TDFXAllocateMemoryLinear(pScrn, pTDFX->overlayBuffer2, new_size)))
pTDFX->whichOverlayBuffer = 0;
TDFXTRACE("TDFXPutImageOverlay: %s have a second overlay buffer for double-buffering\n",
pTDFX->overlayBuffer2 ? "Do" : "Do not");
/* copy data */
top = ya >> 16;
left = (xa >> 16) & ~1;
npixels = ((((xb + 0xffff) >> 16) + 1) & ~1) - left;
/* Get buffer offset */
if (pTDFX->whichOverlayBuffer == 0)
offset = (pTDFX->overlayBuffer->offset * bpp) + (top * dstPitch) + pTDFX->fbOffset;
else
offset = (pTDFX->overlayBuffer2->offset * bpp) + (top * dstPitch) + pTDFX->fbOffset;
/* Flip to other buffer for next time */
pTDFX->whichOverlayBuffer ^= 1;
dst_start = pTDFX->FbBase + offset;
switch(id) {
case FOURCC_YV12:
case FOURCC_I420:
top &= ~1;
dst_start += left << 1;
tmp = ((top >> 1) * srcPitch2) + (left >> 1);
s2offset += tmp;
s3offset += tmp;
if(id == FOURCC_I420) {
tmp = s2offset;
s2offset = s3offset;
s3offset = tmp;
}
nlines = ((((yb + 0xffff) >> 16) + 1) & ~1) - top;
TDFXTRACE("TDFXPutImageOverlay: using copymungeddata\n");
TDFXCopyMungedData(buf + (top * srcPitch) + left, buf + s2offset,
buf + s3offset, dst_start, srcPitch, srcPitch2,
dstPitch, nlines, npixels);
break;
case FOURCC_UYVY:
case FOURCC_YUY2:
default:
left <<= 1;
buf += (top * srcPitch) + left;
nlines = ((yb + 0xffff) >> 16) - top;
dst_start += left;
TDFXTRACE("TDFXPutImageOverlay: using copydata\n");
TDFXCopyData(buf, dst_start, srcPitch, dstPitch, nlines, npixels);
break;
}
if(!REGION_EQUAL(pScrn->pScreen, &pPriv->clip, clipBoxes)) {
REGION_COPY(pScrn->pScreen, &pPriv->clip, clipBoxes);
xf86XVFillKeyHelper(pScrn->pScreen, pPriv->colorKey, clipBoxes);
}
TDFXDisplayVideoOverlay(pScrn, id, offset, width, height, dstPitch, xa, xb, ya, &dstBox, src_w, src_h, drw_w, drw_h);
pPriv->videoStatus = CLIENT_VIDEO_ON;
pTDFX->VideoTimerCallback = TDFXVideoTimerCallback;
/* Display some swap-buffer related info...: vidCurrOverlayStartAddr, fbiSwapHistory */
/* To give us some insight into workings or otherwise of swapbuffer stuff */
TDFXTRACE("TDFXPutImageOverlay: vidCurrOverlayStrtAdr=%x, fbiSwpHist=%x, whchBuf=%d, 3Dstus=%x\n",
pTDFX->readLong(pTDFX, VIDCUROVERLAYSTARTADDR),
pTDFX->readLong(pTDFX, SST_3D_FBISWAPHISTORY),
pTDFX->whichOverlayBuffer,
pTDFX->readLong(pTDFX, SST_3D_STATUS)
);
return Success;
}
static void
TDFXVideoTimerCallback(ScrnInfoPtr pScrn, Time time)
{
TDFXPtr pTDFX = TDFXPTR(pScrn);
TDFXPortPrivPtr pPriv = pTDFX->overlayAdaptor->pPortPrivates[0].ptr;
if(pPriv->videoStatus & TIMER_MASK) {
if(pPriv->videoStatus & OFF_TIMER) {
if(pPriv->offTime < time) {
pTDFX->ModeReg.vidcfg &= ~VIDPROCCFGMASK;
pTDFX->writeLong(pTDFX, VIDPROCCFG, pTDFX->ModeReg.vidcfg);
pPriv->videoStatus = FREE_TIMER;
pPriv->freeTime = time + FREE_DELAY;
}
} else
if(pPriv->videoStatus & FREE_TIMER) {
if(pPriv->freeTime < time) {
if(pTDFX->overlayBuffer) {
xf86FreeOffscreenLinear(pTDFX->overlayBuffer);
pTDFX->overlayBuffer = NULL;
}
if(pTDFX->overlayBuffer2) {
xf86FreeOffscreenLinear(pTDFX->overlayBuffer2);
pTDFX->overlayBuffer2 = NULL;
}
pPriv->videoStatus = 0;
pTDFX->VideoTimerCallback = NULL;
}
}
} else /* shouldn't get here */
pTDFX->VideoTimerCallback = NULL;
}
/*
* MEMORY MANAGEMENT
*/
static FBAreaPtr
TDFXAllocateMemoryArea (ScrnInfoPtr pScrn, FBAreaPtr area, int width, int height)
{
TDFXPtr pTDFX = TDFXPTR(pScrn);
ScreenPtr pScreen;
FBAreaPtr new_area;
if (area) {
if ((area->box.x2 - area->box.x1 >= width) &&
(area->box.y2 - area->box.y1 >= height))
return area;
if (xf86ResizeOffscreenArea(area, width, height))
return area;
xf86FreeOffscreenArea(area);
}
pScreen = xf86ScrnToScreen(pScrn);
new_area = xf86AllocateOffscreenArea(pScreen, width, height, pTDFX->cpp, NULL, NULL, NULL);
if (!new_area) {
int max_width, max_height;
xf86QueryLargestOffscreenArea(pScreen, &max_width, &max_height, pTDFX->cpp, 0, PRIORITY_EXTREME);
if (max_width < width || max_height < height)
return NULL;
xf86PurgeUnlockedOffscreenAreas(pScreen);
new_area = xf86AllocateOffscreenArea(pScreen, width, height, pTDFX->cpp, NULL, NULL, NULL);
}
return new_area;
}
static FBLinearPtr
TDFXAllocateMemoryLinear (ScrnInfoPtr pScrn, FBLinearPtr linear, int size)
{
ScreenPtr pScreen;
FBLinearPtr new_linear;
if(linear) {
if(linear->size >= size)
return linear;
if(xf86ResizeOffscreenLinear(linear, size))
return linear;
xf86FreeOffscreenLinear(linear);
}
pScreen = xf86ScrnToScreen(pScrn);
new_linear = xf86AllocateOffscreenLinear(pScreen, size, 4, NULL, NULL, NULL);
if(!new_linear) {
int max_size;
xf86QueryLargestOffscreenLinear(pScreen, &max_size, 4, PRIORITY_EXTREME);
if(max_size < size)
return NULL;
xf86PurgeUnlockedOffscreenAreas(pScreen);
new_linear = xf86AllocateOffscreenLinear(pScreen, size, 4, NULL, NULL, NULL);
}
return new_linear;
}
/****************** Offscreen stuff ***************/
typedef struct {
FBLinearPtr linear;
Bool isOn;
} OffscreenPrivRec, * OffscreenPrivPtr;
static int
TDFXAllocateSurface(
ScrnInfoPtr pScrn,
int id,
unsigned short w,
unsigned short h,
XF86SurfacePtr surface
){
TDFXPtr pTDFX = TDFXPTR(pScrn);
FBLinearPtr linear;
int pitch, size, bpp;
OffscreenPrivPtr pPriv;
if((w > 2048) || (h > 2048))
return BadAlloc;
w = (w + 1) & ~1;
pitch = ((w << 1) + 15) & ~15;
bpp = pScrn->bitsPerPixel >> 3;
size = ((pitch * h) + bpp - 1) / bpp;
if(!(linear = TDFXAllocateMemoryLinear(pScrn, NULL, size)))
return BadAlloc;
surface->width = w;
surface->height = h;
if(!(surface->pitches = malloc(sizeof(int)))) {
xf86FreeOffscreenLinear(linear);
return BadAlloc;
}
if(!(surface->offsets = malloc(sizeof(int)))) {
free(surface->pitches);
xf86FreeOffscreenLinear(linear);
return BadAlloc;
}
if(!(pPriv = malloc(sizeof(OffscreenPrivRec)))) {
free(surface->pitches);
free(surface->offsets);
xf86FreeOffscreenLinear(linear);
return BadAlloc;
}
pPriv->linear = linear;
pPriv->isOn = FALSE;
surface->pScrn = pScrn;
surface->id = id;
surface->pitches[0] = pitch;
surface->offsets[0] = pTDFX->fbOffset + (linear->offset * bpp);
surface->devPrivate.ptr = (pointer)pPriv;
return Success;
}
static int
TDFXStopSurface(
XF86SurfacePtr surface
){
OffscreenPrivPtr pPriv = (OffscreenPrivPtr)surface->devPrivate.ptr;
if(pPriv->isOn) {
TDFXPtr pTDFX = TDFXPTR(surface->pScrn);
pTDFX->ModeReg.vidcfg &= ~VIDPROCCFGMASK;
pTDFX->writeLong(pTDFX, VIDPROCCFG, pTDFX->ModeReg.vidcfg);
pPriv->isOn = FALSE;
}
return Success;
}
static int
TDFXFreeSurface(
XF86SurfacePtr surface
){
OffscreenPrivPtr pPriv = (OffscreenPrivPtr)surface->devPrivate.ptr;
if(pPriv->isOn)
TDFXStopSurface(surface);
xf86FreeOffscreenLinear(pPriv->linear);
free(surface->pitches);
free(surface->offsets);
free(surface->devPrivate.ptr);
return Success;
}
static int
TDFXGetSurfaceAttribute(
ScrnInfoPtr pScrn,
Atom attribute,
INT32 *value
){
return TDFXGetPortAttributeOverlay(pScrn, attribute, value,
(pointer)(GET_PORT_PRIVATE(pScrn)));
}
static int
TDFXSetSurfaceAttribute(
ScrnInfoPtr pScrn,
Atom attribute,
INT32 value
){
return TDFXSetPortAttributeOverlay(pScrn, attribute, value,
(pointer)(GET_PORT_PRIVATE(pScrn)));
}
static int
TDFXDisplaySurface(
XF86SurfacePtr surface,
short src_x, short src_y,
short drw_x, short drw_y,
short src_w, short src_h,
short drw_w, short drw_h,
RegionPtr clipBoxes
){
OffscreenPrivPtr pPriv = (OffscreenPrivPtr)surface->devPrivate.ptr;
ScrnInfoPtr pScrn = surface->pScrn;
TDFXPtr pTDFX = TDFXPTR(pScrn);
TDFXPortPrivPtr portPriv = pTDFX->overlayAdaptor->pPortPrivates[0].ptr;
INT32 x1, y1, x2, y2;
BoxRec dstBox;
x1 = src_x;
x2 = src_x + src_w;
y1 = src_y;
y2 = src_y + src_h;
dstBox.x1 = drw_x;
dstBox.x2 = drw_x + drw_w;
dstBox.y1 = drw_y;
dstBox.y2 = drw_y + drw_h;
if(!xf86XVClipVideoHelper(&dstBox, &x1, &x2, &y1, &y2, clipBoxes,
surface->width, surface->height))
{
return Success;
}
dstBox.x1 -= pScrn->frameX0;
dstBox.x2 -= pScrn->frameX0;
dstBox.y1 -= pScrn->frameY0;
dstBox.y2 -= pScrn->frameY0;
#if 0
TDFXResetVideoOverlay(pScrn);
#endif
TDFXDisplayVideoOverlay(pScrn, surface->id, surface->offsets[0],
surface->width, surface->height, surface->pitches[0],
x1, y1, x2, &dstBox, src_w, src_h, drw_w, drw_h);
xf86XVFillKeyHelper(pScrn->pScreen, portPriv->colorKey, clipBoxes);
pPriv->isOn = TRUE;
/* we've prempted the XvImage stream so set its free timer */
if(portPriv->videoStatus & CLIENT_VIDEO_ON) {
REGION_EMPTY(pScrn->pScreen, &portPriv->clip);
UpdateCurrentTime();
portPriv->videoStatus = FREE_TIMER;
portPriv->freeTime = currentTime.milliseconds + FREE_DELAY;
pTDFX->VideoTimerCallback = TDFXVideoTimerCallback;
}
return Success;
}
static void
TDFXInitOffscreenImages(ScreenPtr pScreen)
{
XF86OffscreenImagePtr offscreenImages;
/* need to free this someplace */
if(!(offscreenImages = malloc(sizeof(XF86OffscreenImageRec))))
return;
offscreenImages[0].image = &OverlayImages[0];
offscreenImages[0].flags = VIDEO_OVERLAID_IMAGES |
VIDEO_CLIP_TO_VIEWPORT;
offscreenImages[0].alloc_surface = TDFXAllocateSurface;
offscreenImages[0].free_surface = TDFXFreeSurface;
offscreenImages[0].display = TDFXDisplaySurface;
offscreenImages[0].stop = TDFXStopSurface;
offscreenImages[0].setAttribute = TDFXSetSurfaceAttribute;
offscreenImages[0].getAttribute = TDFXGetSurfaceAttribute;
offscreenImages[0].max_width = 2048;
offscreenImages[0].max_height = 2048;
offscreenImages[0].num_attributes = 2;
offscreenImages[0].attributes = OverlayAttributes;
xf86XVRegisterOffscreenImages(pScreen, offscreenImages, 1);
}