blob: 7ccb3fe4734fe5699ef1f351ea0ffacb3e480450 [file] [log] [blame]
#include "xorg_composite.h"
#include "xorg_renderer.h"
#include "xorg_exa_tgsi.h"
#include "cso_cache/cso_context.h"
#include "util/u_format.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, enum pipe_format format)
{
const struct util_format_description *format_desc;
uint8_t packed[4];
format_desc = util_format_description(format);
packed[0] = pixel;
packed[1] = pixel >> 8;
packed[2] = pixel >> 16;
packed[3] = pixel >> 24;
format_desc->unpack_rgba_float(color, 0, packed, 0, 1, 1);
}
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 = xf86ScreenToScrn(pScreen);
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 && 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 && 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, PIPE_FORMAT_B8G8R8A8_UNORM);
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] = {0};
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, PIPE_SHADER_FRAGMENT,
exa->num_bound_samplers,
(const struct pipe_sampler_state **)samplers);
cso_set_sampler_views(exa->renderer->cso, PIPE_SHADER_FRAGMENT,
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, pixmap->tex->format);
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, PIPE_SHADER_FRAGMENT, 0, NULL);
cso_set_sampler_views(exa->renderer->cso, PIPE_SHADER_FRAGMENT, 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;
}