xserver-multidpi/hw/kdrive/mga/g400_composite.c
Eric Anholt e11e60b361 Greatly improve the correctness and performance of the MGA render
implementation. Includes cache flushing to prevent bad first reads of
    the framebuffer, fixing blending of many formats, falling back on many
    unsupported operations, and falling back early to prevent migration.
    Passes all of rendercheck except some of the blend (!) tests.
2005-06-09 21:59:26 +00:00

379 lines
11 KiB
C

/*
* $Id$
*
* Copyright © 2004 Damien Ciabrini
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of Anders Carlsson not be used in
* advertising or publicity pertaining to distribution of the software without
* specific, written prior permission. Anders Carlsson makes no
* representations about the suitability of this software for any purpose. It
* is provided "as is" without express or implied warranty.
*
* DAMIEN CIABRINI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL ANDERS CARLSSON BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Header$ */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "mga.h"
#include "g400_common.h"
static PixmapPtr currentSrc;
static PixmapPtr currentMask;
struct blendinfo {
Bool dst_alpha;
Bool src_alpha;
CARD32 blend_cntl;
};
static struct blendinfo mgaBlendOP[] = {
/* Clear */
{0, 0, MGA_SRC_ZERO | MGA_DST_ZERO},
/* Src */
{0, 0, MGA_SRC_ONE | MGA_DST_ZERO},
/* Dst */
{0, 0, MGA_SRC_ZERO | MGA_DST_ONE},
/* Over */
{0, 1, MGA_SRC_ONE | MGA_DST_ONE_MINUS_SRC_ALPHA},
/* OverReverse */
{1, 0, MGA_SRC_ONE_MINUS_DST_ALPHA | MGA_DST_ONE},
/* In */
{1, 0, MGA_SRC_DST_ALPHA | MGA_DST_ZERO},
/* InReverse */
{0, 1, MGA_SRC_ZERO | MGA_DST_SRC_ALPHA},
/* Out */
{1, 0, MGA_SRC_ONE_MINUS_DST_ALPHA | MGA_DST_ZERO},
/* OutReverse */
{0, 1, MGA_SRC_ZERO | MGA_DST_ONE_MINUS_SRC_ALPHA},
/* Atop */
{1, 1, MGA_SRC_DST_ALPHA | MGA_DST_ONE_MINUS_SRC_ALPHA},
/* AtopReverse */
{1, 1, MGA_SRC_ONE_MINUS_DST_ALPHA | MGA_DST_SRC_ALPHA},
/* Xor */
{1, 1, MGA_SRC_ONE_MINUS_DST_ALPHA | MGA_DST_ONE_MINUS_SRC_ALPHA},
/* Add */
{0, 0, MGA_SRC_ONE | MGA_DST_ONE},
};
struct formatinfo {
int fmt;
CARD32 card_fmt;
};
static struct formatinfo texformats[] = {
{PICT_a8r8g8b8, MGA_TW32},
{PICT_x8r8g8b8, MGA_TW32},
{PICT_r5g6b5, MGA_TW16},
{PICT_a1r5g5b5, MGA_TW15},
{PICT_x1r5g5b5, MGA_TW15},
{PICT_a4r4g4b4, MGA_TW12},
{PICT_x4r4g4b4, MGA_TW12},
{PICT_a8, MGA_TW8A},
};
static int MGA_LOG2( int val )
{
int ret = 0;
while (val >> ret)
ret++;
return ((1 << (ret-1)) == val) ? (ret-1) : ret;
}
static Bool
mgaCheckSourceTexture (int tmu,
PicturePtr pPict)
{
int w = pPict->pDrawable->width;
int h = pPict->pDrawable->height;
int i;
CARD32 texctl = 0;
if ((w > 2047) || (h > 2047))
MGA_FALLBACK(("Picture too large for composition (%dx%d)\n", w, h));
for (i = 0; i < sizeof(texformats) / sizeof(texformats[0]); i++) {
if (texformats[i].fmt == pPict->format) {
texctl = texformats[i].card_fmt;
break;
}
}
if (texctl == 0) {
MGA_FALLBACK(("Picture is in unsupported format 0x%x\n"
pPict->format));
}
return TRUE;
}
static Bool
PrepareSourceTexture (int tmu,
PicturePtr pSrcPicture,
PixmapPtr pSrc)
{
KdScreenPriv (pSrc->drawable.pScreen);
int mem_base=(int)pScreenPriv->screen->memory_base;
int pitch = pSrc->devKind / (pSrc->drawable.bitsPerPixel >> 3);
int i;
int w = pSrc->drawable.width;
int h = pSrc->drawable.height;
int w_log2 = MGA_LOG2(w);
int h_log2 = MGA_LOG2(h);
int texctl = MGA_PITCHLIN | ((pitch & (2048 - 1)) << 9) |
MGA_CLAMPUV | MGA_NOPERSPECTIVE;
int texctl2=MGA_G400_TC2_MAGIC;
for (i = 0; i < sizeof(texformats) / sizeof(texformats[0]); i++) {
if (texformats[i].fmt == pSrcPicture->format) {
texctl |= texformats[i].card_fmt;
break;
}
}
if (PICT_FORMAT_A(pSrcPicture->format) != 0) {
texctl |= MGA_TAKEY;
} else {
texctl |= MGA_TAMASK | MGA_TAKEY;
}
if (tmu == 1)
texctl2 |= MGA_TC2_DUALTEX | MGA_TC2_SELECT_TMU1;
mgaWaitAvail (6);
MGA_OUT32 (mmio, MGA_REG_TEXCTL2, texctl2);
MGA_OUT32 (mmio, MGA_REG_TEXCTL, texctl);
/* Source (texture) address + pitch */
MGA_OUT32 (mmio, MGA_REG_TEXORG, ((int)pSrc->devPrivate.ptr - mem_base));
MGA_OUT32 (mmio, MGA_REG_TEXWIDTH, (w-1)<<18 | ((8-w_log2)&63)<<9 | w_log2);
MGA_OUT32 (mmio, MGA_REG_TEXHEIGHT, (h-1)<<18 | ((8-h_log2)&63)<<9 | h_log2);
/* Disable filtering since we aren't doing stretch blit */
MGA_OUT32 (mmio, MGA_REG_TEXFILTER, (0x10<<21) | MGA_MAG_NRST | MGA_MIN_NRST);
if (tmu == 1) {
mgaWaitAvail (1);
MGA_OUT32 (mmio, MGA_REG_TEXCTL2, MGA_G400_TC2_MAGIC | MGA_TC2_DUALTEX);
}
return TRUE;
}
Bool
mgaCheckComposite(int op, PicturePtr pSrcPicture, PicturePtr pMaskPicture,
PicturePtr pDstPicture)
{
if (op >= sizeof(mgaBlendOP) / sizeof(mgaBlendOP[0]))
MGA_FALLBACK(("unsupported op %x", op));
if (!mgaCheckSourceTexture (0, pSrcPicture))
return FALSE;
if (pSrcPicture->transform != NULL)
MGA_FALLBACK(("Transformed src unsupported"));
if (pMaskPicture != NULL) {
if (PICT_FORMAT_A(pMaskPicture->format) == 0)
MGA_FALLBACK(("Mask without alpha unsupported"));
if (!mgaCheckSourceTexture (1, pMaskPicture))
return FALSE;
if (pMaskPicture->transform != NULL)
MGA_FALLBACK(("Transformed mask unsupported"));
}
if (pMaskPicture->componentAlpha)
MGA_FALLBACK(("Component alpha unsupported"));
switch (pDstPicture->format) {
case PICT_a8:
MGA_FALLBACK(("render to A8 unsupported"));
}
return TRUE;
}
#define C_ARG1_CUR 0x0
#define C_ARG2_DIFFUSE MGA_TDS_COLOR_ARG2_DIFFUSE
#define C_ARG2_PREV MGA_TDS_COLOR_ARG2_PREVSTAGE
#define COLOR_MUL MGA_TDS_COLOR_SEL_MUL
#define COLOR_ARG1 MGA_TDS_COLOR_SEL_ARG1
#define COLOR_ARG2 MGA_TDS_COLOR_SEL_ARG2
#define A_ARG1_CUR 0x0
#define A_ARG2_IGN A_ARG2_DIFFUSE
#define A_ARG2_DIFFUSE MGA_TDS_ALPHA_ARG2_DIFFUSE
#define A_ARG2_PREV MGA_TDS_ALPHA_ARG2_PREVSTAGE
#define ALPHA_MUL MGA_TDS_ALPHA_SEL_MUL
#define ALPHA_ARG1 MGA_TDS_ALPHA_SEL_ARG1
#define ALPHA_ARG2 MGA_TDS_ALPHA_SEL_ARG2
Bool
mgaPrepareComposite (int op,
PicturePtr pSrcPicture,
PicturePtr pMaskPicture,
PicturePtr pDstPicture,
PixmapPtr pSrc,
PixmapPtr pMask,
PixmapPtr pDst)
{
KdScreenPriv (pSrc->drawable.pScreen);
int mem_base=(int)pScreenPriv->screen->memory_base;
int cmd, blendcntl;
mgaWaitIdle();
/* Init MGA (clipping) */
mgaSetup (pSrc->drawable.pScreen, pDst->drawable.bitsPerPixel, 5);
MGA_OUT32 (mmio, MGA_REG_TMR1, 0);
MGA_OUT32 (mmio, MGA_REG_TMR2, 0);
MGA_OUT32 (mmio, MGA_REG_TMR3, 0);
MGA_OUT32 (mmio, MGA_REG_TMR4, 0);
MGA_OUT32 (mmio, MGA_REG_TMR5, 0);
MGA_OUT32 (mmio, MGA_REG_TMR6, 0);
MGA_OUT32 (mmio, MGA_REG_TMR7, 0);
MGA_OUT32 (mmio, MGA_REG_TMR8, 0x10000);
/* Initialize colors to 0, used in the src = A8 case */
mgaWaitAvail(9);
MGA_OUT32 (mmio, MGA_REG_DR4, 0);
MGA_OUT32 (mmio, MGA_REG_DR6, 0);
MGA_OUT32 (mmio, MGA_REG_DR7, 0);
MGA_OUT32 (mmio, MGA_REG_DR8, 0);
MGA_OUT32 (mmio, MGA_REG_DR10, 0);
MGA_OUT32 (mmio, MGA_REG_DR11, 0);
MGA_OUT32 (mmio, MGA_REG_DR12, 0);
MGA_OUT32 (mmio, MGA_REG_DR14, 0);
MGA_OUT32 (mmio, MGA_REG_DR15, 0);
/* Destination flags */
mgaWaitAvail (2);
MGA_OUT32 (mmio, MGA_REG_DSTORG, ((int)pDst->devPrivate.ptr - mem_base));
MGA_OUT32 (mmio, MGA_REG_PITCH,
pDst->devKind / (pDst->drawable.bitsPerPixel >> 3));
/* Source(s) flags */
if (!PrepareSourceTexture (0, pSrcPicture, pSrc)) return FALSE;
if (pMask != NULL)
if (!PrepareSourceTexture (1, pMaskPicture, pMask)) return FALSE;
/* MultiTexture modulation */
mgaWaitAvail (2);
if (pSrcPicture->format == PICT_a8) {
/* C = 0 A = As */
MGA_OUT32 (mmio, MGA_REG_TDUALSTAGE0,
C_ARG2_DIFFUSE | COLOR_ARG2 |
A_ARG1_CUR | ALPHA_ARG1);
} else {
/* C = Cs A = As */
MGA_OUT32 (mmio, MGA_REG_TDUALSTAGE0,
C_ARG1_CUR | COLOR_ARG1 |
A_ARG1_CUR | ALPHA_ARG1);
}
if (pMask != NULL) {
if (PICT_FORMAT_A (pSrcPicture->format) == 0) {
MGA_OUT32 (mmio, MGA_REG_TDUALSTAGE1,
C_ARG1_CUR | C_ARG2_PREV | COLOR_MUL |
A_ARG1_CUR | A_ARG2_IGN | ALPHA_ARG1 |
MGA_TDS_COLOR_ARG1_REPLICATEALPHA);
} else {
MGA_OUT32 (mmio, MGA_REG_TDUALSTAGE1,
C_ARG1_CUR | C_ARG2_PREV | COLOR_MUL |
A_ARG1_CUR | A_ARG2_PREV | ALPHA_MUL |
MGA_TDS_COLOR_ARG1_REPLICATEALPHA);
}
} else {
MGA_OUT32 (mmio, MGA_REG_TDUALSTAGE1, 0);
}
cmd = MGA_OPCOD_TEXTURE_TRAP | MGA_ATYPE_RSTR | 0x000c0000 |
MGA_DWGCTL_SHIFTZERO | MGA_DWGCTL_SGNZERO | MGA_DWGCTL_ARZERO |
MGA_ATYPE_I;
blendcntl = mgaBlendOP[op].blend_cntl;
if (PICT_FORMAT_A(pDstPicture->format) == 0 && mgaBlendOP[op].dst_alpha) {
if ((blendcntl & MGA_SRC_BLEND_MASK) == MGA_SRC_DST_ALPHA)
blendcntl = (blendcntl & ~MGA_SRC_BLEND_MASK) | MGA_SRC_ONE;
else if ((blendcntl & MGA_SRC_BLEND_MASK) == MGA_SRC_ONE_MINUS_DST_ALPHA)
blendcntl = (blendcntl & ~MGA_SRC_BLEND_MASK) | MGA_SRC_ZERO;
}
mgaWaitAvail (2);
MGA_OUT32 (mmio, MGA_REG_DWGCTL, cmd);
MGA_OUT32 (mmio, MGA_REG_ALPHACTRL, MGA_ALPHACHANNEL | blendcntl);
currentSrc=pSrc;
currentMask=pMask;
return TRUE;
}
void
mgaComposite (int srcX,
int srcY,
int maskX,
int maskY,
int dstX,
int dstY,
int width,
int height)
{
int src_w2 = MGA_LOG2 (currentSrc->drawable.width);
int src_h2 = MGA_LOG2 (currentSrc->drawable.height);
/* Source positions can be outside source textures' boundaries.
* We clamp the values here to avoid rendering glitches.
*/
srcX=srcX % currentSrc->drawable.width;
srcY=srcY % currentSrc->drawable.height;
maskX=maskX % currentMask->drawable.width;
maskY=maskY % currentMask->drawable.height;
mgaWaitAvail (4);
/* Start position in the texture */
MGA_OUT32 (mmio, MGA_REG_TMR6, srcX<<(20-src_w2));
MGA_OUT32 (mmio, MGA_REG_TMR7, srcY<<(20-src_h2));
/* Increments (1 since we aren't doing stretch blit) */
MGA_OUT32 (mmio, MGA_REG_TMR0, 1<<(20-src_w2));
MGA_OUT32 (mmio, MGA_REG_TMR3, 1<<(20-src_h2));
if (currentMask != NULL) {
int mask_w2 = MGA_LOG2 (currentMask->drawable.width);
int mask_h2 = MGA_LOG2 (currentMask->drawable.height);
mgaWaitAvail (6);
MGA_OUT32 (mmio, MGA_REG_TEXCTL2,
MGA_G400_TC2_MAGIC | MGA_TC2_DUALTEX | MGA_TC2_SELECT_TMU1);
MGA_OUT32 (mmio, MGA_REG_TMR6, maskX<<(20-mask_w2));
MGA_OUT32 (mmio, MGA_REG_TMR7, maskY<<(20-mask_h2));
MGA_OUT32 (mmio, MGA_REG_TMR0, 1<<(20-mask_w2));
MGA_OUT32 (mmio, MGA_REG_TMR3, 1<<(20-mask_h2));
MGA_OUT32 (mmio, MGA_REG_TEXCTL2, MGA_G400_TC2_MAGIC | MGA_TC2_DUALTEX);
}
/* Destination Bounding Box
* (Boundary Right | Boundary Left, Y dest | Height)
*/
mgaWaitAvail (2);
MGA_OUT32 (mmio, MGA_REG_FXBNDRY,
((dstX + width) << 16) | (dstX & 0xffff));
MGA_OUT32 (mmio, MGA_REG_YDSTLEN | MGA_REG_EXEC,
(dstY << 16) | (height & 0xffff));
}
void
mgaDoneComposite (void)
{
}