#include "xorg_composite.h" #include "xorg_renderer.h" #include "xorg_exa_tgsi.h" #include "cso_cache/cso_context.h" #include "util/u_sampler.h" /*XXX also in Xrender.h but the including it here breaks compilition */ #define XFixedToDouble(f) (((double) (f)) / 65536.) struct xorg_composite_blend { int op : 8; unsigned alpha_dst : 4; unsigned alpha_src : 4; unsigned rgb_src : 8; /**< PIPE_BLENDFACTOR_x */ unsigned rgb_dst : 8; /**< PIPE_BLENDFACTOR_x */ }; #define BLEND_OP_OVER 3 static const struct xorg_composite_blend xorg_blends[] = { { PictOpClear, 0, 0, PIPE_BLENDFACTOR_ZERO, PIPE_BLENDFACTOR_ZERO}, { PictOpSrc, 0, 0, PIPE_BLENDFACTOR_ONE, PIPE_BLENDFACTOR_ZERO}, { PictOpDst, 0, 0, PIPE_BLENDFACTOR_ZERO, PIPE_BLENDFACTOR_ONE}, { PictOpOver, 0, 1, PIPE_BLENDFACTOR_ONE, PIPE_BLENDFACTOR_INV_SRC_ALPHA}, { PictOpOverReverse, 1, 0, PIPE_BLENDFACTOR_INV_DST_ALPHA, PIPE_BLENDFACTOR_ONE}, { PictOpIn, 1, 0, PIPE_BLENDFACTOR_DST_ALPHA, PIPE_BLENDFACTOR_ZERO}, { PictOpInReverse, 0, 1, PIPE_BLENDFACTOR_ZERO, PIPE_BLENDFACTOR_SRC_ALPHA}, { PictOpOut, 1, 0, PIPE_BLENDFACTOR_INV_DST_ALPHA, PIPE_BLENDFACTOR_ZERO}, { PictOpOutReverse, 0, 1, PIPE_BLENDFACTOR_ZERO, PIPE_BLENDFACTOR_INV_SRC_ALPHA}, { PictOpAtop, 1, 1, PIPE_BLENDFACTOR_DST_ALPHA, PIPE_BLENDFACTOR_INV_SRC_ALPHA}, { PictOpAtopReverse, 1, 1, PIPE_BLENDFACTOR_INV_DST_ALPHA, PIPE_BLENDFACTOR_SRC_ALPHA}, { PictOpXor, 1, 1, PIPE_BLENDFACTOR_INV_DST_ALPHA, PIPE_BLENDFACTOR_INV_SRC_ALPHA}, { PictOpAdd, 0, 0, PIPE_BLENDFACTOR_ONE, PIPE_BLENDFACTOR_ONE}, }; static INLINE void pixel_to_float4(Pixel pixel, float *color) { CARD32 r, g, b, a; a = (pixel >> 24) & 0xff; r = (pixel >> 16) & 0xff; g = (pixel >> 8) & 0xff; b = (pixel >> 0) & 0xff; color[0] = ((float)r) / 255.; color[1] = ((float)g) / 255.; color[2] = ((float)b) / 255.; color[3] = ((float)a) / 255.; } static boolean blend_for_op(struct xorg_composite_blend *blend, int op, PicturePtr pSrcPicture, PicturePtr pMaskPicture, PicturePtr pDstPicture) { const int num_blends = sizeof(xorg_blends)/sizeof(struct xorg_composite_blend); int i; boolean supported = FALSE; /* our default in case something goes wrong */ *blend = xorg_blends[BLEND_OP_OVER]; for (i = 0; i < num_blends; ++i) { if (xorg_blends[i].op == op) { *blend = xorg_blends[i]; supported = TRUE; } } /* If there's no dst alpha channel, adjust the blend op so that we'll treat * it as always 1. */ if (pDstPicture && PICT_FORMAT_A(pDstPicture->format) == 0 && blend->alpha_dst) { if (blend->rgb_src == PIPE_BLENDFACTOR_DST_ALPHA) blend->rgb_src = PIPE_BLENDFACTOR_ONE; else if (blend->rgb_src == PIPE_BLENDFACTOR_INV_DST_ALPHA) blend->rgb_src = PIPE_BLENDFACTOR_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 (pMaskPicture && pMaskPicture->componentAlpha && PICT_FORMAT_RGB(pMaskPicture->format) && blend->alpha_src) { if (blend->rgb_dst == PIPE_BLENDFACTOR_SRC_ALPHA) { blend->rgb_dst = PIPE_BLENDFACTOR_SRC_COLOR; } else if (blend->rgb_dst == PIPE_BLENDFACTOR_INV_SRC_ALPHA) { blend->rgb_dst = PIPE_BLENDFACTOR_INV_SRC_COLOR; } } return supported; } static INLINE int render_repeat_to_gallium(int mode) { switch(mode) { case RepeatNone: return PIPE_TEX_WRAP_CLAMP_TO_BORDER; case RepeatNormal: return PIPE_TEX_WRAP_REPEAT; case RepeatReflect: return PIPE_TEX_WRAP_MIRROR_REPEAT; case RepeatPad: return PIPE_TEX_WRAP_CLAMP_TO_EDGE; default: debug_printf("Unsupported repeat mode\n"); } return PIPE_TEX_WRAP_REPEAT; } static INLINE boolean render_filter_to_gallium(int xrender_filter, int *out_filter) { switch (xrender_filter) { case PictFilterNearest: *out_filter = PIPE_TEX_FILTER_NEAREST; break; case PictFilterBilinear: *out_filter = PIPE_TEX_FILTER_LINEAR; break; case PictFilterFast: *out_filter = PIPE_TEX_FILTER_NEAREST; break; case PictFilterGood: *out_filter = PIPE_TEX_FILTER_LINEAR; break; case PictFilterBest: *out_filter = PIPE_TEX_FILTER_LINEAR; break; case PictFilterConvolution: *out_filter = PIPE_TEX_FILTER_NEAREST; return FALSE; default: debug_printf("Unknown xrender filter\n"); *out_filter = PIPE_TEX_FILTER_NEAREST; return FALSE; } return TRUE; } static boolean is_filter_accelerated(PicturePtr pic) { int filter; if (pic && !render_filter_to_gallium(pic->filter, &filter)) return FALSE; return TRUE; } boolean xorg_composite_accelerated(int op, PicturePtr pSrcPicture, PicturePtr pMaskPicture, PicturePtr pDstPicture) { ScreenPtr pScreen = pDstPicture->pDrawable->pScreen; ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; modesettingPtr ms = modesettingPTR(pScrn); struct xorg_composite_blend blend; if (!is_filter_accelerated(pSrcPicture) || !is_filter_accelerated(pMaskPicture)) { XORG_FALLBACK("Unsupported Xrender filter"); } if (pSrcPicture->pSourcePict) { if (pSrcPicture->pSourcePict->type != SourcePictTypeSolidFill) XORG_FALLBACK("Gradients not enabled (haven't been well tested)"); } if (blend_for_op(&blend, op, pSrcPicture, pMaskPicture, pDstPicture)) { /* Check for component alpha */ if (pMaskPicture && pMaskPicture->componentAlpha && PICT_FORMAT_RGB(pMaskPicture->format)) { if (blend.alpha_src && blend.rgb_src != PIPE_BLENDFACTOR_ZERO) { XORG_FALLBACK("Component alpha not supported with source " "alpha and source value blending. (op=%d)", op); } } return TRUE; } XORG_FALLBACK("Unsupported composition operation = %d", op); } static void bind_blend_state(struct exa_context *exa, int op, PicturePtr pSrcPicture, PicturePtr pMaskPicture, PicturePtr pDstPicture) { struct xorg_composite_blend blend_opt; struct pipe_blend_state blend; blend_for_op(&blend_opt, op, pSrcPicture, pMaskPicture, pDstPicture); memset(&blend, 0, sizeof(struct pipe_blend_state)); blend.rt[0].blend_enable = 1; blend.rt[0].colormask = PIPE_MASK_RGBA; blend.rt[0].rgb_src_factor = blend_opt.rgb_src; blend.rt[0].alpha_src_factor = blend_opt.rgb_src; blend.rt[0].rgb_dst_factor = blend_opt.rgb_dst; blend.rt[0].alpha_dst_factor = blend_opt.rgb_dst; cso_set_blend(exa->renderer->cso, &blend); } static unsigned picture_format_fixups(struct exa_pixmap_priv *pSrc, PicturePtr pSrcPicture, boolean mask, PicturePtr pDstPicture) { boolean set_alpha = FALSE; boolean swizzle = FALSE; unsigned ret = 0; if (pSrc->picture_format == pSrcPicture->format) { if (pSrc->picture_format == PICT_a8) { if (mask) return FS_MASK_LUMINANCE; else if (pDstPicture->format != PICT_a8) { /* if both dst and src are luminance then * we don't want to swizzle the alpha (X) of the * source into W component of the dst because * it will break our destination */ return FS_SRC_LUMINANCE; } } return 0; } if (pSrc->picture_format != PICT_a8r8g8b8) { assert(!"can not handle formats"); return 0; } /* pSrc->picture_format == PICT_a8r8g8b8 */ switch (pSrcPicture->format) { case PICT_x8b8g8r8: case PICT_b8g8r8: set_alpha = TRUE; /* fall trough */ case PICT_a8b8g8r8: swizzle = TRUE; break; case PICT_x8r8g8b8: case PICT_r8g8b8: set_alpha = TRUE; /* fall through */ case PICT_a8r8g8b8: break; #ifdef PICT_TYPE_BGRA case PICT_b8g8r8a8: case PICT_b8g8r8x8: case PICT_a2r10g10b10: case PICT_x2r10g10b10: case PICT_a2b10g10r10: case PICT_x2b10g10r10: #endif default: assert(!"can not handle formats"); return 0; } if (set_alpha) ret |= mask ? FS_MASK_SET_ALPHA : FS_SRC_SET_ALPHA; if (swizzle) ret |= mask ? FS_MASK_SWIZZLE_RGB : FS_SRC_SWIZZLE_RGB; return ret; } static void bind_shaders(struct exa_context *exa, int op, PicturePtr pSrcPicture, PicturePtr pMaskPicture, PicturePtr pDstPicture, struct exa_pixmap_priv *pSrc, struct exa_pixmap_priv *pMask) { unsigned vs_traits = 0, fs_traits = 0; struct xorg_shader shader; exa->has_solid_color = FALSE; if (pSrcPicture) { if (pSrcPicture->repeatType == RepeatNone && pSrcPicture->transform) fs_traits |= FS_SRC_REPEAT_NONE; if (pSrcPicture->pSourcePict) { if (pSrcPicture->pSourcePict->type == SourcePictTypeSolidFill) { fs_traits |= FS_SOLID_FILL; vs_traits |= VS_SOLID_FILL; debug_assert(pSrcPicture->format == PICT_a8r8g8b8); pixel_to_float4(pSrcPicture->pSourcePict->solidFill.color, exa->solid_color); exa->has_solid_color = TRUE; } else { debug_assert("!gradients not supported"); } } else { fs_traits |= FS_COMPOSITE; vs_traits |= VS_COMPOSITE; } fs_traits |= picture_format_fixups(pSrc, pSrcPicture, FALSE, pDstPicture); } if (pMaskPicture) { vs_traits |= VS_MASK; fs_traits |= FS_MASK; if (pMaskPicture->repeatType == RepeatNone && pMaskPicture->transform) fs_traits |= FS_MASK_REPEAT_NONE; if (pMaskPicture->componentAlpha) { struct xorg_composite_blend blend; blend_for_op(&blend, op, pSrcPicture, pMaskPicture, NULL); if (blend.alpha_src) { fs_traits |= FS_CA_SRCALPHA; } else fs_traits |= FS_CA_FULL; } fs_traits |= picture_format_fixups(pMask, pMaskPicture, TRUE, pDstPicture); } shader = xorg_shaders_get(exa->renderer->shaders, vs_traits, fs_traits); cso_set_vertex_shader_handle(exa->renderer->cso, shader.vs); cso_set_fragment_shader_handle(exa->renderer->cso, shader.fs); } static void bind_samplers(struct exa_context *exa, int op, PicturePtr pSrcPicture, PicturePtr pMaskPicture, PicturePtr pDstPicture, struct exa_pixmap_priv *pSrc, struct exa_pixmap_priv *pMask, struct exa_pixmap_priv *pDst) { struct pipe_sampler_state *samplers[PIPE_MAX_SAMPLERS]; struct pipe_sampler_state src_sampler, mask_sampler; struct pipe_sampler_view view_templ; struct pipe_sampler_view *src_view; struct pipe_context *pipe = exa->pipe; exa->num_bound_samplers = 0; memset(&src_sampler, 0, sizeof(struct pipe_sampler_state)); memset(&mask_sampler, 0, sizeof(struct pipe_sampler_state)); if (pSrcPicture && pSrc) { if (exa->has_solid_color) { debug_assert(!"solid color with textures"); samplers[0] = NULL; pipe_sampler_view_reference(&exa->bound_sampler_views[0], NULL); } else { unsigned src_wrap = render_repeat_to_gallium( pSrcPicture->repeatType); int filter; render_filter_to_gallium(pSrcPicture->filter, &filter); src_sampler.wrap_s = src_wrap; src_sampler.wrap_t = src_wrap; src_sampler.min_img_filter = filter; src_sampler.mag_img_filter = filter; src_sampler.min_mip_filter = PIPE_TEX_MIPFILTER_NEAREST; src_sampler.normalized_coords = 1; samplers[0] = &src_sampler; exa->num_bound_samplers = 1; u_sampler_view_default_template(&view_templ, pSrc->tex, pSrc->tex->format); src_view = pipe->create_sampler_view(pipe, pSrc->tex, &view_templ); pipe_sampler_view_reference(&exa->bound_sampler_views[0], NULL); exa->bound_sampler_views[0] = src_view; } } if (pMaskPicture && pMask) { unsigned mask_wrap = render_repeat_to_gallium( pMaskPicture->repeatType); int filter; render_filter_to_gallium(pMaskPicture->filter, &filter); mask_sampler.wrap_s = mask_wrap; mask_sampler.wrap_t = mask_wrap; mask_sampler.min_img_filter = filter; mask_sampler.mag_img_filter = filter; src_sampler.min_mip_filter = PIPE_TEX_MIPFILTER_NEAREST; mask_sampler.normalized_coords = 1; samplers[1] = &mask_sampler; exa->num_bound_samplers = 2; u_sampler_view_default_template(&view_templ, pMask->tex, pMask->tex->format); src_view = pipe->create_sampler_view(pipe, pMask->tex, &view_templ); pipe_sampler_view_reference(&exa->bound_sampler_views[1], NULL); exa->bound_sampler_views[1] = src_view; } cso_set_samplers(exa->renderer->cso, exa->num_bound_samplers, (const struct pipe_sampler_state **)samplers); cso_set_fragment_sampler_views(exa->renderer->cso, exa->num_bound_samplers, exa->bound_sampler_views); } static INLINE boolean matrix_from_pict_transform(PictTransform *trans, float *matrix) { if (!trans) return FALSE; matrix[0] = XFixedToDouble(trans->matrix[0][0]); matrix[3] = XFixedToDouble(trans->matrix[0][1]); matrix[6] = XFixedToDouble(trans->matrix[0][2]); matrix[1] = XFixedToDouble(trans->matrix[1][0]); matrix[4] = XFixedToDouble(trans->matrix[1][1]); matrix[7] = XFixedToDouble(trans->matrix[1][2]); matrix[2] = XFixedToDouble(trans->matrix[2][0]); matrix[5] = XFixedToDouble(trans->matrix[2][1]); matrix[8] = XFixedToDouble(trans->matrix[2][2]); return TRUE; } static void setup_transforms(struct exa_context *exa, PicturePtr pSrcPicture, PicturePtr pMaskPicture) { PictTransform *src_t = NULL; PictTransform *mask_t = NULL; if (pSrcPicture) src_t = pSrcPicture->transform; if (pMaskPicture) mask_t = pMaskPicture->transform; exa->transform.has_src = matrix_from_pict_transform(src_t, exa->transform.src); exa->transform.has_mask = matrix_from_pict_transform(mask_t, exa->transform.mask); } boolean xorg_composite_bind_state(struct exa_context *exa, int op, PicturePtr pSrcPicture, PicturePtr pMaskPicture, PicturePtr pDstPicture, struct exa_pixmap_priv *pSrc, struct exa_pixmap_priv *pMask, struct exa_pixmap_priv *pDst) { struct pipe_surface *dst_surf = xorg_gpu_surface(exa->pipe, pDst); renderer_bind_destination(exa->renderer, dst_surf, pDst->width, pDst->height); bind_blend_state(exa, op, pSrcPicture, pMaskPicture, pDstPicture); bind_shaders(exa, op, pSrcPicture, pMaskPicture, pDstPicture, pSrc, pMask); bind_samplers(exa, op, pSrcPicture, pMaskPicture, pDstPicture, pSrc, pMask, pDst); setup_transforms(exa, pSrcPicture, pMaskPicture); if (exa->num_bound_samplers == 0 ) { /* solid fill */ renderer_begin_solid(exa->renderer); } else { renderer_begin_textures(exa->renderer, exa->num_bound_samplers); } pipe_surface_reference(&dst_surf, NULL); return TRUE; } void xorg_composite(struct exa_context *exa, struct exa_pixmap_priv *dst, int srcX, int srcY, int maskX, int maskY, int dstX, int dstY, int width, int height) { if (exa->num_bound_samplers == 0 ) { /* solid fill */ renderer_solid(exa->renderer, dstX, dstY, dstX + width, dstY + height, exa->solid_color); } else { int pos[6] = {srcX, srcY, maskX, maskY, dstX, dstY}; float *src_matrix = NULL; float *mask_matrix = NULL; if (exa->transform.has_src) src_matrix = exa->transform.src; if (exa->transform.has_mask) mask_matrix = exa->transform.mask; renderer_texture(exa->renderer, pos, width, height, exa->bound_sampler_views, exa->num_bound_samplers, src_matrix, mask_matrix); } } boolean xorg_solid_bind_state(struct exa_context *exa, struct exa_pixmap_priv *pixmap, Pixel fg) { struct pipe_surface *dst_surf = xorg_gpu_surface(exa->pipe, pixmap); unsigned vs_traits, fs_traits; struct xorg_shader shader; pixel_to_float4(fg, exa->solid_color); exa->has_solid_color = TRUE; #if 0 debug_printf("Color Pixel=(%d, %d, %d, %d), RGBA=(%f, %f, %f, %f)\n", (fg >> 24) & 0xff, (fg >> 16) & 0xff, (fg >> 8) & 0xff, (fg >> 0) & 0xff, exa->solid_color[0], exa->solid_color[1], exa->solid_color[2], exa->solid_color[3]); #endif vs_traits = VS_SOLID_FILL; fs_traits = FS_SOLID_FILL; renderer_bind_destination(exa->renderer, dst_surf, pixmap->width, pixmap->height); bind_blend_state(exa, PictOpSrc, NULL, NULL, NULL); cso_set_samplers(exa->renderer->cso, 0, NULL); cso_set_fragment_sampler_views(exa->renderer->cso, 0, NULL); shader = xorg_shaders_get(exa->renderer->shaders, vs_traits, fs_traits); cso_set_vertex_shader_handle(exa->renderer->cso, shader.vs); cso_set_fragment_shader_handle(exa->renderer->cso, shader.fs); renderer_begin_solid(exa->renderer); pipe_surface_reference(&dst_surf, NULL); return TRUE; } void xorg_solid(struct exa_context *exa, struct exa_pixmap_priv *pixmap, int x0, int y0, int x1, int y1) { renderer_solid(exa->renderer, x0, y0, x1, y1, exa->solid_color); } void xorg_composite_done(struct exa_context *exa) { renderer_draw_flush(exa->renderer); exa->transform.has_src = FALSE; exa->transform.has_mask = FALSE; exa->has_solid_color = FALSE; exa->num_bound_samplers = 0; }