428261197a
Tested by ajacoutot@, krw@, shadchin@ and jasper@ on various configurations including multihead with both zaphod and xrandr.
524 lines
14 KiB
C
524 lines
14 KiB
C
/*
|
|
* Copyright © 2006 Intel Corporation
|
|
*
|
|
* 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, sublicense,
|
|
* 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 NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS 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.
|
|
*
|
|
* Authors:
|
|
* Eric Anholt <eric@anholt.net>
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <kdrive-config.h>
|
|
#endif
|
|
|
|
#include "ephyr.h"
|
|
#include "exa_priv.h"
|
|
#include "fbpict.h"
|
|
|
|
#define EPHYR_TRACE_DRAW 0
|
|
|
|
#if EPHYR_TRACE_DRAW
|
|
#define TRACE_DRAW() ErrorF("%s\n", __FUNCTION__);
|
|
#else
|
|
#define TRACE_DRAW() do { } while (0)
|
|
#endif
|
|
|
|
/* Use some oddball alignments, to expose issues in alignment handling in EXA. */
|
|
#define EPHYR_OFFSET_ALIGN 24
|
|
#define EPHYR_PITCH_ALIGN 24
|
|
|
|
#define EPHYR_OFFSCREEN_SIZE (16 * 1024 * 1024)
|
|
#define EPHYR_OFFSCREEN_BASE (1 * 1024 * 1024)
|
|
|
|
/**
|
|
* Forces a real devPrivate.ptr for hidden pixmaps, so that we can call down to
|
|
* fb functions.
|
|
*/
|
|
static void
|
|
ephyrPreparePipelinedAccess(PixmapPtr pPix, int index)
|
|
{
|
|
KdScreenPriv(pPix->drawable.pScreen);
|
|
KdScreenInfo *screen = pScreenPriv->screen;
|
|
EphyrScrPriv *scrpriv = screen->driver;
|
|
EphyrFakexaPriv *fakexa = scrpriv->fakexa;
|
|
|
|
assert(fakexa->saved_ptrs[index] == NULL);
|
|
fakexa->saved_ptrs[index] = pPix->devPrivate.ptr;
|
|
|
|
if (pPix->devPrivate.ptr != NULL)
|
|
return;
|
|
|
|
pPix->devPrivate.ptr = fakexa->exa->memoryBase + exaGetPixmapOffset(pPix);
|
|
}
|
|
|
|
/**
|
|
* Restores the original devPrivate.ptr of the pixmap from before we messed with
|
|
* it.
|
|
*/
|
|
static void
|
|
ephyrFinishPipelinedAccess(PixmapPtr pPix, int index)
|
|
{
|
|
KdScreenPriv(pPix->drawable.pScreen);
|
|
KdScreenInfo *screen = pScreenPriv->screen;
|
|
EphyrScrPriv *scrpriv = screen->driver;
|
|
EphyrFakexaPriv *fakexa = scrpriv->fakexa;
|
|
|
|
pPix->devPrivate.ptr = fakexa->saved_ptrs[index];
|
|
fakexa->saved_ptrs[index] = NULL;
|
|
}
|
|
|
|
/**
|
|
* Sets up a scratch GC for fbFill, and saves other parameters for the
|
|
* ephyrSolid implementation.
|
|
*/
|
|
static Bool
|
|
ephyrPrepareSolid(PixmapPtr pPix, int alu, Pixel pm, Pixel fg)
|
|
{
|
|
ScreenPtr pScreen = pPix->drawable.pScreen;
|
|
KdScreenPriv(pScreen);
|
|
KdScreenInfo *screen = pScreenPriv->screen;
|
|
EphyrScrPriv *scrpriv = screen->driver;
|
|
EphyrFakexaPriv *fakexa = scrpriv->fakexa;
|
|
ChangeGCVal tmpval[3];
|
|
|
|
ephyrPreparePipelinedAccess(pPix, EXA_PREPARE_DEST);
|
|
|
|
fakexa->pDst = pPix;
|
|
fakexa->pGC = GetScratchGC(pPix->drawable.depth, pScreen);
|
|
|
|
tmpval[0].val = alu;
|
|
tmpval[1].val = pm;
|
|
tmpval[2].val = fg;
|
|
ChangeGC(NullClient, fakexa->pGC, GCFunction | GCPlaneMask | GCForeground, tmpval);
|
|
|
|
ValidateGC(&pPix->drawable, fakexa->pGC);
|
|
|
|
TRACE_DRAW();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Does an fbFill of the rectangle to be drawn.
|
|
*/
|
|
static void
|
|
ephyrSolid(PixmapPtr pPix, int x1, int y1, int x2, int y2)
|
|
{
|
|
ScreenPtr pScreen = pPix->drawable.pScreen;
|
|
KdScreenPriv(pScreen);
|
|
KdScreenInfo *screen = pScreenPriv->screen;
|
|
EphyrScrPriv *scrpriv = screen->driver;
|
|
EphyrFakexaPriv *fakexa = scrpriv->fakexa;
|
|
|
|
fbFill(&fakexa->pDst->drawable, fakexa->pGC, x1, y1, x2 - x1, y2 - y1);
|
|
}
|
|
|
|
/**
|
|
* Cleans up the scratch GC created in ephyrPrepareSolid.
|
|
*/
|
|
static void
|
|
ephyrDoneSolid(PixmapPtr pPix)
|
|
{
|
|
ScreenPtr pScreen = pPix->drawable.pScreen;
|
|
KdScreenPriv(pScreen);
|
|
KdScreenInfo *screen = pScreenPriv->screen;
|
|
EphyrScrPriv *scrpriv = screen->driver;
|
|
EphyrFakexaPriv *fakexa = scrpriv->fakexa;
|
|
|
|
FreeScratchGC(fakexa->pGC);
|
|
|
|
ephyrFinishPipelinedAccess(pPix, EXA_PREPARE_DEST);
|
|
}
|
|
|
|
/**
|
|
* Sets up a scratch GC for fbCopyArea, and saves other parameters for the
|
|
* ephyrCopy implementation.
|
|
*/
|
|
static Bool
|
|
ephyrPrepareCopy(PixmapPtr pSrc, PixmapPtr pDst, int dx, int dy, int alu,
|
|
Pixel pm)
|
|
{
|
|
ScreenPtr pScreen = pDst->drawable.pScreen;
|
|
KdScreenPriv(pScreen);
|
|
KdScreenInfo *screen = pScreenPriv->screen;
|
|
EphyrScrPriv *scrpriv = screen->driver;
|
|
EphyrFakexaPriv *fakexa = scrpriv->fakexa;
|
|
ChangeGCVal tmpval[2];
|
|
|
|
ephyrPreparePipelinedAccess(pDst, EXA_PREPARE_DEST);
|
|
ephyrPreparePipelinedAccess(pSrc, EXA_PREPARE_SRC);
|
|
|
|
fakexa->pSrc = pSrc;
|
|
fakexa->pDst = pDst;
|
|
fakexa->pGC = GetScratchGC(pDst->drawable.depth, pScreen);
|
|
|
|
tmpval[0].val = alu;
|
|
tmpval[1].val = pm;
|
|
ChangeGC (NullClient, fakexa->pGC, GCFunction | GCPlaneMask, tmpval);
|
|
|
|
ValidateGC(&pDst->drawable, fakexa->pGC);
|
|
|
|
TRACE_DRAW();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Does an fbCopyArea to take care of the requested copy.
|
|
*/
|
|
static void
|
|
ephyrCopy(PixmapPtr pDst, int srcX, int srcY, int dstX, int dstY, int w, int h)
|
|
{
|
|
ScreenPtr pScreen = pDst->drawable.pScreen;
|
|
KdScreenPriv(pScreen);
|
|
KdScreenInfo *screen = pScreenPriv->screen;
|
|
EphyrScrPriv *scrpriv = screen->driver;
|
|
EphyrFakexaPriv *fakexa = scrpriv->fakexa;
|
|
|
|
fbCopyArea(&fakexa->pSrc->drawable, &fakexa->pDst->drawable, fakexa->pGC,
|
|
srcX, srcY, w, h, dstX, dstY);
|
|
}
|
|
|
|
/**
|
|
* Cleans up the scratch GC created in ephyrPrepareCopy.
|
|
*/
|
|
static void
|
|
ephyrDoneCopy(PixmapPtr pDst)
|
|
{
|
|
ScreenPtr pScreen = pDst->drawable.pScreen;
|
|
KdScreenPriv(pScreen);
|
|
KdScreenInfo *screen = pScreenPriv->screen;
|
|
EphyrScrPriv *scrpriv = screen->driver;
|
|
EphyrFakexaPriv *fakexa = scrpriv->fakexa;
|
|
|
|
FreeScratchGC (fakexa->pGC);
|
|
|
|
ephyrFinishPipelinedAccess(fakexa->pSrc, EXA_PREPARE_SRC);
|
|
ephyrFinishPipelinedAccess(fakexa->pDst, EXA_PREPARE_DEST);
|
|
}
|
|
|
|
/**
|
|
* Reports that we can always accelerate the given operation. This may not be
|
|
* desirable from an EXA testing standpoint -- testing the fallback paths would
|
|
* be useful, too.
|
|
*/
|
|
static Bool
|
|
ephyrCheckComposite(int op, PicturePtr pSrcPicture, PicturePtr pMaskPicture,
|
|
PicturePtr pDstPicture)
|
|
{
|
|
/* Exercise the component alpha helper, so fail on this case like a normal
|
|
* driver
|
|
*/
|
|
if (pMaskPicture && pMaskPicture->componentAlpha && op == PictOpOver)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Saves off the parameters for ephyrComposite.
|
|
*/
|
|
static Bool
|
|
ephyrPrepareComposite(int op, PicturePtr pSrcPicture, PicturePtr pMaskPicture,
|
|
PicturePtr pDstPicture, PixmapPtr pSrc, PixmapPtr pMask,
|
|
PixmapPtr pDst)
|
|
{
|
|
KdScreenPriv(pDst->drawable.pScreen);
|
|
KdScreenInfo *screen = pScreenPriv->screen;
|
|
EphyrScrPriv *scrpriv = screen->driver;
|
|
EphyrFakexaPriv *fakexa = scrpriv->fakexa;
|
|
|
|
ephyrPreparePipelinedAccess(pDst, EXA_PREPARE_DEST);
|
|
ephyrPreparePipelinedAccess(pSrc, EXA_PREPARE_SRC);
|
|
if (pMask != NULL)
|
|
ephyrPreparePipelinedAccess(pMask, EXA_PREPARE_MASK);
|
|
|
|
fakexa->op = op;
|
|
fakexa->pSrcPicture = pSrcPicture;
|
|
fakexa->pMaskPicture = pMaskPicture;
|
|
fakexa->pDstPicture = pDstPicture;
|
|
fakexa->pSrc = pSrc;
|
|
fakexa->pMask = pMask;
|
|
fakexa->pDst = pDst;
|
|
|
|
TRACE_DRAW();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Does an fbComposite to complete the requested drawing operation.
|
|
*/
|
|
static void
|
|
ephyrComposite(PixmapPtr pDst, int srcX, int srcY, int maskX, int maskY,
|
|
int dstX, int dstY, int w, int h)
|
|
{
|
|
KdScreenPriv(pDst->drawable.pScreen);
|
|
KdScreenInfo *screen = pScreenPriv->screen;
|
|
EphyrScrPriv *scrpriv = screen->driver;
|
|
EphyrFakexaPriv *fakexa = scrpriv->fakexa;
|
|
|
|
fbComposite(fakexa->op, fakexa->pSrcPicture, fakexa->pMaskPicture,
|
|
fakexa->pDstPicture, srcX, srcY, maskX, maskY, dstX, dstY,
|
|
w, h);
|
|
}
|
|
|
|
static void
|
|
ephyrDoneComposite(PixmapPtr pDst)
|
|
{
|
|
KdScreenPriv(pDst->drawable.pScreen);
|
|
KdScreenInfo *screen = pScreenPriv->screen;
|
|
EphyrScrPriv *scrpriv = screen->driver;
|
|
EphyrFakexaPriv *fakexa = scrpriv->fakexa;
|
|
|
|
if (fakexa->pMask != NULL)
|
|
ephyrFinishPipelinedAccess(fakexa->pMask, EXA_PREPARE_MASK);
|
|
ephyrFinishPipelinedAccess(fakexa->pSrc, EXA_PREPARE_SRC);
|
|
ephyrFinishPipelinedAccess(fakexa->pDst, EXA_PREPARE_DEST);
|
|
}
|
|
|
|
/**
|
|
* Does fake acceleration of DownloadFromScren using memcpy.
|
|
*/
|
|
static Bool
|
|
ephyrDownloadFromScreen(PixmapPtr pSrc, int x, int y, int w, int h, char *dst,
|
|
int dst_pitch)
|
|
{
|
|
KdScreenPriv(pSrc->drawable.pScreen);
|
|
KdScreenInfo *screen = pScreenPriv->screen;
|
|
EphyrScrPriv *scrpriv = screen->driver;
|
|
EphyrFakexaPriv *fakexa = scrpriv->fakexa;
|
|
unsigned char *src;
|
|
int src_pitch, cpp;
|
|
|
|
if (pSrc->drawable.bitsPerPixel < 8)
|
|
return FALSE;
|
|
|
|
ephyrPreparePipelinedAccess(pSrc, EXA_PREPARE_SRC);
|
|
|
|
cpp = pSrc->drawable.bitsPerPixel / 8;
|
|
src_pitch = exaGetPixmapPitch(pSrc);
|
|
src = fakexa->exa->memoryBase + exaGetPixmapOffset(pSrc);
|
|
src += y * src_pitch + x * cpp;
|
|
|
|
for (; h > 0; h--) {
|
|
memcpy(dst, src, w * cpp);
|
|
dst += dst_pitch;
|
|
src += src_pitch;
|
|
}
|
|
|
|
exaMarkSync(pSrc->drawable.pScreen);
|
|
|
|
ephyrFinishPipelinedAccess(pSrc, EXA_PREPARE_SRC);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Does fake acceleration of UploadToScreen using memcpy.
|
|
*/
|
|
static Bool
|
|
ephyrUploadToScreen(PixmapPtr pDst, int x, int y, int w, int h, char *src,
|
|
int src_pitch)
|
|
{
|
|
KdScreenPriv(pDst->drawable.pScreen);
|
|
KdScreenInfo *screen = pScreenPriv->screen;
|
|
EphyrScrPriv *scrpriv = screen->driver;
|
|
EphyrFakexaPriv *fakexa = scrpriv->fakexa;
|
|
unsigned char *dst;
|
|
int dst_pitch, cpp;
|
|
|
|
if (pDst->drawable.bitsPerPixel < 8)
|
|
return FALSE;
|
|
|
|
ephyrPreparePipelinedAccess(pDst, EXA_PREPARE_DEST);
|
|
|
|
cpp = pDst->drawable.bitsPerPixel / 8;
|
|
dst_pitch = exaGetPixmapPitch(pDst);
|
|
dst = fakexa->exa->memoryBase + exaGetPixmapOffset(pDst);
|
|
dst += y * dst_pitch + x * cpp;
|
|
|
|
for (; h > 0; h--) {
|
|
memcpy(dst, src, w * cpp);
|
|
dst += dst_pitch;
|
|
src += src_pitch;
|
|
}
|
|
|
|
exaMarkSync(pDst->drawable.pScreen);
|
|
|
|
ephyrFinishPipelinedAccess(pDst, EXA_PREPARE_DEST);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static Bool
|
|
ephyrPrepareAccess(PixmapPtr pPix, int index)
|
|
{
|
|
/* Make sure we don't somehow end up with a pointer that is in framebuffer
|
|
* and hasn't been readied for us.
|
|
*/
|
|
assert(pPix->devPrivate.ptr != NULL);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* In fakexa, we currently only track whether we have synced to the latest
|
|
* "accelerated" drawing that has happened or not. It's not used for anything
|
|
* yet.
|
|
*/
|
|
static int
|
|
ephyrMarkSync(ScreenPtr pScreen)
|
|
{
|
|
KdScreenPriv(pScreen);
|
|
KdScreenInfo *screen = pScreenPriv->screen;
|
|
EphyrScrPriv *scrpriv = screen->driver;
|
|
EphyrFakexaPriv *fakexa = scrpriv->fakexa;
|
|
|
|
fakexa->is_synced = FALSE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Assumes that we're waiting on the latest marker. When EXA gets smarter and
|
|
* starts using markers in a fine-grained way (for example, waiting on drawing
|
|
* to required pixmaps to complete, rather than waiting for all drawing to
|
|
* complete), we'll want to make the ephyrMarkSync/ephyrWaitMarker
|
|
* implementation fine-grained as well.
|
|
*/
|
|
static void
|
|
ephyrWaitMarker(ScreenPtr pScreen, int marker)
|
|
{
|
|
KdScreenPriv(pScreen);
|
|
KdScreenInfo *screen = pScreenPriv->screen;
|
|
EphyrScrPriv *scrpriv = screen->driver;
|
|
EphyrFakexaPriv *fakexa = scrpriv->fakexa;
|
|
|
|
fakexa->is_synced = TRUE;
|
|
}
|
|
|
|
/**
|
|
* This function initializes EXA to use the fake acceleration implementation
|
|
* which just falls through to software. The purpose is to have a reliable,
|
|
* correct driver with which to test changes to the EXA core.
|
|
*/
|
|
Bool
|
|
ephyrDrawInit(ScreenPtr pScreen)
|
|
{
|
|
KdScreenPriv(pScreen);
|
|
KdScreenInfo *screen = pScreenPriv->screen;
|
|
EphyrScrPriv *scrpriv = screen->driver;
|
|
EphyrPriv *priv = screen->card->driver;
|
|
EphyrFakexaPriv *fakexa;
|
|
Bool success;
|
|
|
|
fakexa = calloc(1, sizeof(*fakexa));
|
|
if (fakexa == NULL)
|
|
return FALSE;
|
|
|
|
fakexa->exa = exaDriverAlloc();
|
|
if (fakexa->exa == NULL) {
|
|
free(fakexa);
|
|
return FALSE;
|
|
}
|
|
|
|
fakexa->exa->memoryBase = (CARD8 *) (priv->base);
|
|
fakexa->exa->memorySize = priv->bytes_per_line * ephyrBufferHeight(screen);
|
|
fakexa->exa->offScreenBase = priv->bytes_per_line * screen->height;
|
|
|
|
/* Since we statically link against EXA, we shouldn't have to be smart about
|
|
* versioning.
|
|
*/
|
|
fakexa->exa->exa_major = 2;
|
|
fakexa->exa->exa_minor = 0;
|
|
|
|
fakexa->exa->PrepareSolid = ephyrPrepareSolid;
|
|
fakexa->exa->Solid = ephyrSolid;
|
|
fakexa->exa->DoneSolid = ephyrDoneSolid;
|
|
|
|
fakexa->exa->PrepareCopy = ephyrPrepareCopy;
|
|
fakexa->exa->Copy = ephyrCopy;
|
|
fakexa->exa->DoneCopy = ephyrDoneCopy;
|
|
|
|
fakexa->exa->CheckComposite = ephyrCheckComposite;
|
|
fakexa->exa->PrepareComposite = ephyrPrepareComposite;
|
|
fakexa->exa->Composite = ephyrComposite;
|
|
fakexa->exa->DoneComposite = ephyrDoneComposite;
|
|
|
|
fakexa->exa->DownloadFromScreen = ephyrDownloadFromScreen;
|
|
fakexa->exa->UploadToScreen = ephyrUploadToScreen;
|
|
|
|
fakexa->exa->MarkSync = ephyrMarkSync;
|
|
fakexa->exa->WaitMarker = ephyrWaitMarker;
|
|
|
|
fakexa->exa->PrepareAccess = ephyrPrepareAccess;
|
|
|
|
fakexa->exa->pixmapOffsetAlign = EPHYR_OFFSET_ALIGN;
|
|
fakexa->exa->pixmapPitchAlign = EPHYR_PITCH_ALIGN;
|
|
|
|
fakexa->exa->maxX = 1023;
|
|
fakexa->exa->maxY = 1023;
|
|
|
|
fakexa->exa->flags = EXA_OFFSCREEN_PIXMAPS;
|
|
|
|
success = exaDriverInit(pScreen, fakexa->exa);
|
|
if (success) {
|
|
ErrorF("Initialized fake EXA acceleration\n");
|
|
scrpriv->fakexa = fakexa;
|
|
} else {
|
|
ErrorF("Failed to initialize EXA\n");
|
|
free(fakexa->exa);
|
|
free(fakexa);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
void
|
|
ephyrDrawEnable(ScreenPtr pScreen)
|
|
{
|
|
}
|
|
|
|
void
|
|
ephyrDrawDisable(ScreenPtr pScreen)
|
|
{
|
|
}
|
|
|
|
void
|
|
ephyrDrawFini(ScreenPtr pScreen)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* exaDDXDriverInit is required by the top-level EXA module, and is used by
|
|
* the xorg DDX to hook in its EnableDisableFB wrapper. We don't need it, since
|
|
* we won't be enabling/disabling the FB.
|
|
*/
|
|
void
|
|
exaDDXDriverInit(ScreenPtr pScreen)
|
|
{
|
|
ExaScreenPriv(pScreen);
|
|
|
|
pExaScr->migration = ExaMigrationSmart;
|
|
pExaScr->checkDirtyCorrectness = TRUE;
|
|
}
|