diff options
Diffstat (limited to 'src/i915_render.c')
-rw-r--r-- | src/i915_render.c | 477 |
1 files changed, 477 insertions, 0 deletions
diff --git a/src/i915_render.c b/src/i915_render.c new file mode 100644 index 00000000..2fb41ad1 --- /dev/null +++ b/src/i915_render.c @@ -0,0 +1,477 @@ +/* + * 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: + * Wang Zhenyu <zhenyu.z.wang@intel.com> + * Eric Anholt <eric@anholt.net> + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "xf86.h" +#include "i830.h" +#include "i915_reg.h" +#include "i915_3d.h" + +#ifdef I830DEBUG +#define DEBUG_I830FALLBACK 1 +#endif + +#ifdef DEBUG_I830FALLBACK +#define I830FALLBACK(s, arg...) \ +do { \ + DPRINTF(PFX, "EXA fallback: " s "\n", ##arg); \ + return FALSE; \ +} while(0) +#else +#define I830FALLBACK(s, arg...) \ +do { \ + return FALSE; \ +} while(0) +#endif + +struct formatinfo { + int fmt; + CARD32 card_fmt; +}; + +struct blendinfo { + Bool dst_alpha; + Bool src_alpha; + CARD32 src_blend; + CARD32 dst_blend; +}; + +static struct blendinfo I915BlendOp[] = { + /* Clear */ + {0, 0, BLENDFACT_ZERO, BLENDFACT_ZERO}, + /* Src */ + {0, 0, BLENDFACT_ONE, BLENDFACT_ZERO}, + /* Dst */ + {0, 0, BLENDFACT_ZERO, BLENDFACT_ONE}, + /* Over */ + {0, 1, BLENDFACT_ONE, BLENDFACT_INV_SRC_ALPHA}, + /* OverReverse */ + {1, 0, BLENDFACT_INV_DST_ALPHA, BLENDFACT_ONE}, + /* In */ + {1, 0, BLENDFACT_DST_ALPHA, BLENDFACT_ZERO}, + /* InReverse */ + {0, 1, BLENDFACT_ZERO, BLENDFACT_SRC_ALPHA}, + /* Out */ + {1, 0, BLENDFACT_INV_DST_ALPHA, BLENDFACT_ZERO}, + /* OutReverse */ + {0, 1, BLENDFACT_ZERO, BLENDFACT_INV_SRC_ALPHA}, + /* Atop */ + {1, 1, BLENDFACT_DST_ALPHA, BLENDFACT_INV_SRC_ALPHA}, + /* AtopReverse */ + {1, 1, BLENDFACT_INV_DST_ALPHA, BLENDFACT_SRC_ALPHA}, + /* Xor */ + {1, 1, BLENDFACT_INV_DST_ALPHA, BLENDFACT_INV_SRC_ALPHA}, + /* Add */ + {0, 0, BLENDFACT_ONE, BLENDFACT_ONE}, +}; + +static struct formatinfo I915TexFormats[] = { + {PICT_a8r8g8b8, MAPSURF_32BIT | MT_32BIT_ARGB8888 }, + {PICT_x8r8g8b8, MAPSURF_32BIT | MT_32BIT_XRGB8888 }, + {PICT_a8b8g8r8, MAPSURF_32BIT | MT_32BIT_ABGR8888 }, + {PICT_x8b8g8r8, MAPSURF_32BIT | MT_32BIT_XBGR8888 }, + {PICT_r5g6b5, MAPSURF_16BIT | MT_16BIT_RGB565 }, + {PICT_a1r5g5b5, MAPSURF_16BIT | MT_16BIT_ARGB1555 }, + {PICT_x1r5g5b5, MAPSURF_16BIT | MT_16BIT_ARGB1555 }, + {PICT_a4r4g4b4, MAPSURF_16BIT | MT_16BIT_ARGB4444 }, + {PICT_x4r4g4b4, MAPSURF_16BIT | MT_16BIT_ARGB4444 }, + {PICT_a8, MAPSURF_8BIT | MT_8BIT_A8 }, +}; + +static CARD32 I915GetBlendCntl(int op, PicturePtr pMask, CARD32 dst_format) +{ + CARD32 sblend, dblend; + + sblend = I915BlendOp[op].src_blend; + dblend = I915BlendOp[op].dst_blend; + + /* If there's no dst alpha channel, adjust the blend op so that we'll treat + * it as always 1. + */ + if (PICT_FORMAT_A(dst_format) == 0 && I915BlendOp[op].dst_alpha) { + if (sblend == BLENDFACT_DST_ALPHA) + sblend = BLENDFACT_ONE; + else if (sblend == BLENDFACT_INV_DST_ALPHA) + sblend = BLENDFACT_ZERO; + } + + /* If the source alpha is being used, then we should only be in a case where + * the source blend factor is 0, and the source blend value is the mask + * channels multiplied by the source picture's alpha. + */ + if (pMask && pMask->componentAlpha && PICT_FORMAT_RGB(pMask->format) && + I915BlendOp[op].src_alpha) + { + if (dblend == BLENDFACT_SRC_ALPHA) { + dblend = BLENDFACT_SRC_COLR; + } else if (dblend == BLENDFACT_INV_SRC_ALPHA) { + dblend = BLENDFACT_INV_SRC_COLR; + } + } + + return (sblend << S6_CBUF_SRC_BLEND_FACT_SHIFT) | + (dblend << S6_CBUF_DST_BLEND_FACT_SHIFT); +} + +static Bool I915GetDestFormat(PicturePtr pDstPicture, CARD32 *dst_format) +{ + switch (pDstPicture->format) { + case PICT_a8r8g8b8: + case PICT_x8r8g8b8: + *dst_format = COLR_BUF_ARGB8888; + break; + case PICT_r5g6b5: + *dst_format = COLR_BUF_RGB565; + break; + case PICT_a1r5g5b5: + case PICT_x1r5g5b5: + *dst_format = COLR_BUF_ARGB1555; + break; + /* COLR_BUF_8BIT is special for YUV surfaces. While we may end up being + * able to use it depending on how the hardware implements it, disable it + * for now while we don't know what exactly it does (what channel does it + * read from? + */ + /* + case PICT_a8: + *dst_format = COLR_BUF_8BIT; + break; + */ + case PICT_a4r4g4b4: + case PICT_x4r4g4b4: + *dst_format = COLR_BUF_ARGB4444; + break; + default: + I830FALLBACK("Unsupported dest format 0x%x\n", + (int)pDstPicture->format); + } + + return TRUE; +} + +static Bool I915CheckCompositeTexture(PicturePtr pPict, int unit) +{ + int w = pPict->pDrawable->width; + int h = pPict->pDrawable->height; + int i; + + if ((w > 0x7ff) || (h > 0x7ff)) + I830FALLBACK("Picture w/h too large (%dx%d)\n", w, h); + + for (i = 0; i < sizeof(I915TexFormats) / sizeof(I915TexFormats[0]); i++) + { + if (I915TexFormats[i].fmt == pPict->format) + break; + } + if (i == sizeof(I915TexFormats) / sizeof(I915TexFormats[0])) + I830FALLBACK("Unsupported picture format 0x%x\n", + (int)pPict->format); + + if (pPict->repeat && pPict->repeatType != RepeatNormal) + I830FALLBACK("extended repeat (%d) not supported\n", + pPict->repeatType); + + if (pPict->filter != PictFilterNearest && + pPict->filter != PictFilterBilinear) + I830FALLBACK("Unsupported filter 0x%x\n", pPict->filter); + + return TRUE; +} + +Bool +I915EXACheckComposite(int op, PicturePtr pSrcPicture, PicturePtr pMaskPicture, + PicturePtr pDstPicture) +{ + CARD32 tmp1; + + /* Check for unsupported compositing operations. */ + if (op >= sizeof(I915BlendOp) / sizeof(I915BlendOp[0])) + I830FALLBACK("Unsupported Composite op 0x%x\n", op); + if (pMaskPicture != NULL && pMaskPicture->componentAlpha && + PICT_FORMAT_RGB(pMaskPicture->format)) + { + /* Check if it's component alpha that relies on a source alpha and on + * the source value. We can only get one of those into the single + * source value that we get to blend with. + */ + if (I915BlendOp[op].src_alpha && + (I915BlendOp[op].src_blend != BLENDFACT_ZERO)) + I830FALLBACK("Component alpha not supported with source " + "alpha and source value blending.\n"); + } + + if (!I915CheckCompositeTexture(pSrcPicture, 0)) + I830FALLBACK("Check Src picture texture\n"); + if (pMaskPicture != NULL && !I915CheckCompositeTexture(pMaskPicture, 1)) + I830FALLBACK("Check Mask picture texture\n"); + + if (!I915GetDestFormat(pDstPicture, &tmp1)) + I830FALLBACK("Get Color buffer format\n"); + + return TRUE; +} + +static Bool +I915TextureSetup(PicturePtr pPict, PixmapPtr pPix, int unit) +{ + ScrnInfoPtr pScrn = xf86Screens[pPict->pDrawable->pScreen->myNum]; + I830Ptr pI830 = I830PTR(pScrn); + CARD32 format, offset, pitch, filter; + int w, h, i; + CARD32 wrap_mode = TEXCOORDMODE_CLAMP_BORDER; + + offset = intel_get_pixmap_offset(pPix); + pitch = intel_get_pixmap_pitch(pPix); + w = pPict->pDrawable->width; + h = pPict->pDrawable->height; + pI830->scale_units[unit][0] = pPix->drawable.width; + pI830->scale_units[unit][1] = pPix->drawable.height; + + for (i = 0; i < sizeof(I915TexFormats) / sizeof(I915TexFormats[0]); i++) { + if (I915TexFormats[i].fmt == pPict->format) + break; + } + if (i == sizeof(I915TexFormats)/ sizeof(I915TexFormats[0])) + I830FALLBACK("unknown texture format\n"); + format = I915TexFormats[i].card_fmt; + + if (pPict->repeat) + wrap_mode = TEXCOORDMODE_WRAP; + + switch (pPict->filter) { + case PictFilterNearest: + filter = (FILTER_NEAREST << SS2_MAG_FILTER_SHIFT) | + (FILTER_NEAREST << SS2_MIN_FILTER_SHIFT); + break; + case PictFilterBilinear: + filter = (FILTER_LINEAR << SS2_MAG_FILTER_SHIFT) | + (FILTER_LINEAR << SS2_MIN_FILTER_SHIFT); + break; + default: + filter = 0; + I830FALLBACK("Bad filter 0x%x\n", pPict->filter); + } + + pI830->mapstate[unit * 3 + 0] = offset; + pI830->mapstate[unit * 3 + 1] = format | + ((pPix->drawable.height - 1) << MS3_HEIGHT_SHIFT) | + ((pPix->drawable.width - 1) << MS3_WIDTH_SHIFT); + if (!pI830->disableTiling) + pI830->samplerstate[unit * 3 + 1] |= MS3_USE_FENCE_REGS; + pI830->mapstate[unit * 3 + 2] = ((pitch / 4) - 1) << MS4_PITCH_SHIFT; + + pI830->samplerstate[unit * 3 + 0] = (MIPFILTER_NONE << SS2_MIP_FILTER_SHIFT); + pI830->samplerstate[unit * 3 + 0] |= filter; + pI830->samplerstate[unit * 3 + 1] = SS3_NORMALIZED_COORDS; + pI830->samplerstate[unit * 3 + 1] |= wrap_mode << SS3_TCX_ADDR_MODE_SHIFT; + pI830->samplerstate[unit * 3 + 1] |= wrap_mode << SS3_TCY_ADDR_MODE_SHIFT; + pI830->samplerstate[unit * 3 + 1] |= unit << SS3_TEXTUREMAP_INDEX_SHIFT; + pI830->samplerstate[unit * 3 + 2] = 0x00000000; /* border color */ + + pI830->transform[unit] = pPict->transform; + + return TRUE; +} + +Bool +I915EXAPrepareComposite(int op, PicturePtr pSrcPicture, + PicturePtr pMaskPicture, PicturePtr pDstPicture, + PixmapPtr pSrc, PixmapPtr pMask, PixmapPtr pDst) +{ + ScrnInfoPtr pScrn = xf86Screens[pSrcPicture->pDrawable->pScreen->myNum]; + I830Ptr pI830 = I830PTR(pScrn); + CARD32 dst_format, dst_offset, dst_pitch; + CARD32 blendctl; + +#ifdef I830DEBUG + ErrorF("Enter i915 prepareComposite\n"); +#endif + + pI830->last_3d = LAST_3D_RENDER; + + I915GetDestFormat(pDstPicture, &dst_format); + dst_offset = intel_get_pixmap_offset(pDst); + dst_pitch = intel_get_pixmap_pitch(pDst); + FS_LOCALS(20); + + if (!I915TextureSetup(pSrcPicture, pSrc, 0)) + I830FALLBACK("fail to setup src texture\n"); + if (pMask != NULL) { + if (!I915TextureSetup(pMaskPicture, pMask, 1)) + I830FALLBACK("fail to setup mask texture\n"); + } else { + pI830->transform[1] = NULL; + pI830->scale_units[1][0] = -1; + pI830->scale_units[1][1] = -1; + } + + if (pMask == NULL) { + BEGIN_LP_RING(10); + OUT_RING(_3DSTATE_MAP_STATE | 3); + OUT_RING(0x00000001); /* map 0 */ + OUT_RING(pI830->mapstate[0]); + OUT_RING(pI830->mapstate[1]); + OUT_RING(pI830->mapstate[2]); + + OUT_RING(_3DSTATE_SAMPLER_STATE | 3); + OUT_RING(0x00000001); /* sampler 0 */ + OUT_RING(pI830->samplerstate[0]); + OUT_RING(pI830->samplerstate[1]); + OUT_RING(pI830->samplerstate[2]); + ADVANCE_LP_RING(); + } else { + BEGIN_LP_RING(16); + OUT_RING(_3DSTATE_MAP_STATE | 6); + OUT_RING(0x00000003); /* map 0,1 */ + OUT_RING(pI830->mapstate[0]); + OUT_RING(pI830->mapstate[1]); + OUT_RING(pI830->mapstate[2]); + OUT_RING(pI830->mapstate[3]); + OUT_RING(pI830->mapstate[4]); + OUT_RING(pI830->mapstate[5]); + + OUT_RING(_3DSTATE_SAMPLER_STATE | 6); + OUT_RING(0x00000003); /* sampler 0,1 */ + OUT_RING(pI830->samplerstate[0]); + OUT_RING(pI830->samplerstate[1]); + OUT_RING(pI830->samplerstate[2]); + OUT_RING(pI830->samplerstate[3]); + OUT_RING(pI830->samplerstate[4]); + OUT_RING(pI830->samplerstate[5]); + ADVANCE_LP_RING(); + } + { + CARD32 ss2; + + BEGIN_LP_RING(18); + /* color buffer + * XXX: Need to add USE_FENCE if we ever tile the X Server's pixmaps or + * visible screen. + */ + OUT_RING(_3DSTATE_BUF_INFO_CMD); + OUT_RING(BUF_3D_ID_COLOR_BACK| BUF_3D_PITCH(dst_pitch)); + OUT_RING(BUF_3D_ADDR(dst_offset)); + + OUT_RING(_3DSTATE_DST_BUF_VARS_CMD); + OUT_RING(dst_format); + + OUT_RING(_3DSTATE_LOAD_STATE_IMMEDIATE_1 | I1_LOAD_S(2) | + I1_LOAD_S(4) | I1_LOAD_S(5) | I1_LOAD_S(6) | 3); + ss2 = S2_TEXCOORD_FMT(0, TEXCOORDFMT_2D); + if (pMask) + ss2 |= S2_TEXCOORD_FMT(1, TEXCOORDFMT_2D); + else + ss2 |= S2_TEXCOORD_FMT(1, TEXCOORDFMT_NOT_PRESENT); + ss2 |= S2_TEXCOORD_FMT(2, TEXCOORDFMT_NOT_PRESENT); + ss2 |= S2_TEXCOORD_FMT(3, TEXCOORDFMT_NOT_PRESENT); + ss2 |= S2_TEXCOORD_FMT(4, TEXCOORDFMT_NOT_PRESENT); + ss2 |= S2_TEXCOORD_FMT(5, TEXCOORDFMT_NOT_PRESENT); + ss2 |= S2_TEXCOORD_FMT(6, TEXCOORDFMT_NOT_PRESENT); + ss2 |= S2_TEXCOORD_FMT(7, TEXCOORDFMT_NOT_PRESENT); + OUT_RING(ss2); + OUT_RING((1 << S4_POINT_WIDTH_SHIFT) | S4_LINE_WIDTH_ONE | + S4_CULLMODE_NONE| S4_VFMT_XY); + blendctl = I915GetBlendCntl(op, pMaskPicture, pDstPicture->format); + OUT_RING(0x00000000); /* Disable stencil buffer */ + OUT_RING(S6_CBUF_BLEND_ENABLE | S6_COLOR_WRITE_ENABLE | + (BLENDFUNC_ADD << S6_CBUF_BLEND_FUNC_SHIFT) | blendctl); + + /* issue a flush */ + OUT_RING(MI_FLUSH | MI_WRITE_DIRTY_STATE | MI_INVALIDATE_MAP_CACHE); + OUT_RING(MI_NOOP); + + /* draw rect is unconditional */ + OUT_RING(_3DSTATE_DRAW_RECT_CMD); + OUT_RING(0x00000000); + OUT_RING(0x00000000); /* ymin, xmin*/ + OUT_RING(DRAW_YMAX(pDst->drawable.height - 1) | + DRAW_XMAX(pDst->drawable.width - 1)); + OUT_RING(0x00000000); /* yorig, xorig (relate to color buffer?)*/ + OUT_RING(MI_NOOP); + ADVANCE_LP_RING(); + } + + FS_BEGIN(); + + /* Declare the registers necessary for our program. I don't think the + * S then T ordering is necessary. + */ + i915_fs_dcl(FS_S0); + if (pMask) + i915_fs_dcl(FS_S1); + i915_fs_dcl(FS_T0); + if (pMask) + i915_fs_dcl(FS_T1); + + /* Load the pSrcPicture texel */ + i915_fs_texld(FS_R0, FS_S0, FS_T0); + /* If the texture lacks an alpha channel, force the alpha to 1. */ + if (PICT_FORMAT_A(pSrcPicture->format) == 0) + i915_fs_mov_masked(FS_R0, MASK_W, i915_fs_operand_one()); + + if (!pMask) { + /* No mask, so move to output color */ + i915_fs_mov(FS_OC, i915_fs_operand_reg(FS_R0)); + } else { + /* Load the pMaskPicture texel */ + i915_fs_texld(FS_R1, FS_S1, FS_T1); + /* If the texture lacks an alpha channel, force the alpha to 1. */ + if (PICT_FORMAT_A(pMaskPicture->format) == 0) + i915_fs_mov_masked(FS_R1, MASK_W, i915_fs_operand_one()); + + /* If component alpha is active in the mask and the blend operation + * uses the source alpha, then we know we don't need the source + * value (otherwise we would have hit a fallback earlier), so we + * provide the source alpha (src.A * mask.X) as output color. + * Conversely, if CA is set and we don't need the source alpha, then + * we produce the source value (src.X * mask.X) and the source alpha + * is unused.. Otherwise, we provide the non-CA source value + * (src.X * mask.A). + */ + if (pMaskPicture->componentAlpha && + PICT_FORMAT_RGB(pMaskPicture->format)) + { + if (I915BlendOp[op].src_alpha) { + i915_fs_mul(FS_OC, i915_fs_operand(FS_R0, W, W, W, W), + i915_fs_operand_reg(FS_R1)); + } else { + i915_fs_mul(FS_OC, i915_fs_operand_reg(FS_R0), + i915_fs_operand_reg(FS_R1)); + } + } else { + i915_fs_mul(FS_OC, i915_fs_operand_reg(FS_R0), + i915_fs_operand(FS_R1, W, W, W, W)); + } + } + FS_END(); + + return TRUE; +} |