1/********************************************************** 2 * Copyright 2009-2011 VMware, Inc. All rights reserved. 3 * 4 * Permission is hereby granted, free of charge, to any person 5 * obtaining a copy of this software and associated documentation 6 * files (the "Software"), to deal in the Software without 7 * restriction, including without limitation the rights to use, copy, 8 * modify, merge, publish, distribute, sublicense, and/or sell copies 9 * of the Software, and to permit persons to whom the Software is 10 * furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice shall be 13 * included in all copies or substantial portions of the Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 19 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 20 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 * SOFTWARE. 23 * 24 ********************************************************* 25 * Authors: 26 * Zack Rusin <zackr-at-vmware-dot-com> 27 * Thomas Hellstrom <thellstrom-at-vmware-dot-com> 28 */ 29 30#include "xa_composite.h" 31#include "xa_context.h" 32#include "xa_priv.h" 33#include "cso_cache/cso_context.h" 34#include "util/u_sampler.h" 35#include "util/u_inlines.h" 36 37 38/*XXX also in Xrender.h but the including it here breaks compilition */ 39#define XFixedToDouble(f) (((double) (f)) / 65536.) 40 41struct xa_composite_blend { 42 unsigned op : 8; 43 44 unsigned alpha_dst : 4; 45 unsigned alpha_src : 4; 46 47 unsigned rgb_src : 8; /**< PIPE_BLENDFACTOR_x */ 48 unsigned rgb_dst : 8; /**< PIPE_BLENDFACTOR_x */ 49}; 50 51#define XA_BLEND_OP_OVER 3 52static const struct xa_composite_blend xa_blends[] = { 53 { xa_op_clear, 54 0, 0, PIPE_BLENDFACTOR_ZERO, PIPE_BLENDFACTOR_ZERO}, 55 { xa_op_src, 56 0, 0, PIPE_BLENDFACTOR_ONE, PIPE_BLENDFACTOR_ZERO}, 57 { xa_op_dst, 58 0, 0, PIPE_BLENDFACTOR_ZERO, PIPE_BLENDFACTOR_ONE}, 59 { xa_op_over, 60 0, 1, PIPE_BLENDFACTOR_ONE, PIPE_BLENDFACTOR_INV_SRC_ALPHA}, 61 { xa_op_over_reverse, 62 1, 0, PIPE_BLENDFACTOR_INV_DST_ALPHA, PIPE_BLENDFACTOR_ONE}, 63 { xa_op_in, 64 1, 0, PIPE_BLENDFACTOR_DST_ALPHA, PIPE_BLENDFACTOR_ZERO}, 65 { xa_op_in_reverse, 66 0, 1, PIPE_BLENDFACTOR_ZERO, PIPE_BLENDFACTOR_SRC_ALPHA}, 67 { xa_op_out, 68 1, 0, PIPE_BLENDFACTOR_INV_DST_ALPHA, PIPE_BLENDFACTOR_ZERO}, 69 { xa_op_out_reverse, 70 0, 1, PIPE_BLENDFACTOR_ZERO, PIPE_BLENDFACTOR_INV_SRC_ALPHA}, 71 { xa_op_atop, 72 1, 1, PIPE_BLENDFACTOR_DST_ALPHA, PIPE_BLENDFACTOR_INV_SRC_ALPHA}, 73 { xa_op_atop_reverse, 74 1, 1, PIPE_BLENDFACTOR_INV_DST_ALPHA, PIPE_BLENDFACTOR_SRC_ALPHA}, 75 { xa_op_xor, 76 1, 1, PIPE_BLENDFACTOR_INV_DST_ALPHA, PIPE_BLENDFACTOR_INV_SRC_ALPHA}, 77 { xa_op_add, 78 0, 0, PIPE_BLENDFACTOR_ONE, PIPE_BLENDFACTOR_ONE}, 79}; 80 81/* 82 * The alpha value stored in a L8 texture is read by the 83 * hardware as color, and R8 is read as red. The source alpha value 84 * at the end of the fragment shader is stored in all color channels, 85 * so the correct approach is to blend using DST_COLOR instead of 86 * DST_ALPHA and then output any color channel (L8) or the red channel (R8). 87 */ 88static unsigned 89xa_convert_blend_for_luminance(unsigned factor) 90{ 91 switch(factor) { 92 case PIPE_BLENDFACTOR_DST_ALPHA: 93 return PIPE_BLENDFACTOR_DST_COLOR; 94 case PIPE_BLENDFACTOR_INV_DST_ALPHA: 95 return PIPE_BLENDFACTOR_INV_DST_COLOR; 96 default: 97 break; 98 } 99 return factor; 100} 101 102static boolean 103blend_for_op(struct xa_composite_blend *blend, 104 enum xa_composite_op op, 105 struct xa_picture *src_pic, 106 struct xa_picture *mask_pic, 107 struct xa_picture *dst_pic) 108{ 109 const int num_blends = 110 sizeof(xa_blends)/sizeof(struct xa_composite_blend); 111 int i; 112 boolean supported = FALSE; 113 114 /* 115 * our default in case something goes wrong 116 */ 117 *blend = xa_blends[XA_BLEND_OP_OVER]; 118 119 for (i = 0; i < num_blends; ++i) { 120 if (xa_blends[i].op == op) { 121 *blend = xa_blends[i]; 122 supported = TRUE; 123 break; 124 } 125 } 126 127 /* 128 * No component alpha yet. 129 */ 130 if (mask_pic && mask_pic->component_alpha && blend->alpha_src) 131 return FALSE; 132 133 if (!dst_pic->srf) 134 return supported; 135 136 if ((dst_pic->srf->tex->format == PIPE_FORMAT_L8_UNORM || 137 dst_pic->srf->tex->format == PIPE_FORMAT_R8_UNORM)) { 138 blend->rgb_src = xa_convert_blend_for_luminance(blend->rgb_src); 139 blend->rgb_dst = xa_convert_blend_for_luminance(blend->rgb_dst); 140 } 141 142 /* 143 * If there's no dst alpha channel, adjust the blend op so that we'll treat 144 * it as always 1. 145 */ 146 147 if (xa_format_a(dst_pic->pict_format) == 0 && blend->alpha_dst) { 148 if (blend->rgb_src == PIPE_BLENDFACTOR_DST_ALPHA) 149 blend->rgb_src = PIPE_BLENDFACTOR_ONE; 150 else if (blend->rgb_src == PIPE_BLENDFACTOR_INV_DST_ALPHA) 151 blend->rgb_src = PIPE_BLENDFACTOR_ZERO; 152 } 153 154 return supported; 155} 156 157 158static inline int 159xa_repeat_to_gallium(int mode) 160{ 161 switch(mode) { 162 case xa_wrap_clamp_to_border: 163 return PIPE_TEX_WRAP_CLAMP_TO_BORDER; 164 case xa_wrap_repeat: 165 return PIPE_TEX_WRAP_REPEAT; 166 case xa_wrap_mirror_repeat: 167 return PIPE_TEX_WRAP_MIRROR_REPEAT; 168 case xa_wrap_clamp_to_edge: 169 return PIPE_TEX_WRAP_CLAMP_TO_EDGE; 170 default: 171 break; 172 } 173 return PIPE_TEX_WRAP_REPEAT; 174} 175 176static inline boolean 177xa_filter_to_gallium(int xrender_filter, int *out_filter) 178{ 179 180 switch (xrender_filter) { 181 case xa_filter_nearest: 182 *out_filter = PIPE_TEX_FILTER_NEAREST; 183 break; 184 case xa_filter_linear: 185 *out_filter = PIPE_TEX_FILTER_LINEAR; 186 break; 187 default: 188 *out_filter = PIPE_TEX_FILTER_NEAREST; 189 return FALSE; 190 } 191 return TRUE; 192} 193 194static int 195xa_is_filter_accelerated(struct xa_picture *pic) 196{ 197 int filter; 198 if (pic && !xa_filter_to_gallium(pic->filter, &filter)) 199 return 0; 200 return 1; 201} 202 203/** 204 * xa_src_pict_is_accelerated - Check whether we support acceleration 205 * of the given src_pict type 206 * 207 * \param src_pic[in]: Pointer to a union xa_source_pict to check. 208 * 209 * \returns TRUE if accelerated, FALSE otherwise. 210 */ 211static boolean 212xa_src_pict_is_accelerated(const union xa_source_pict *src_pic) 213{ 214 if (!src_pic) 215 return TRUE; 216 217 if (src_pic->type == xa_src_pict_solid_fill || 218 src_pic->type == xa_src_pict_float_solid_fill) 219 return TRUE; 220 221 return FALSE; 222} 223 224XA_EXPORT int 225xa_composite_check_accelerated(const struct xa_composite *comp) 226{ 227 struct xa_picture *src_pic = comp->src; 228 struct xa_picture *mask_pic = comp->mask; 229 struct xa_composite_blend blend; 230 231 if (!xa_is_filter_accelerated(src_pic) || 232 !xa_is_filter_accelerated(comp->mask)) { 233 return -XA_ERR_INVAL; 234 } 235 236 if (!xa_src_pict_is_accelerated(src_pic->src_pict) || 237 (mask_pic && !xa_src_pict_is_accelerated(mask_pic->src_pict))) 238 return -XA_ERR_INVAL; 239 240 if (!blend_for_op(&blend, comp->op, comp->src, comp->mask, comp->dst)) 241 return -XA_ERR_INVAL; 242 243 /* 244 * No component alpha yet. 245 */ 246 if (mask_pic && mask_pic->component_alpha && blend.alpha_src) 247 return -XA_ERR_INVAL; 248 249 return XA_ERR_NONE; 250} 251 252static int 253bind_composite_blend_state(struct xa_context *ctx, 254 const struct xa_composite *comp) 255{ 256 struct xa_composite_blend blend_opt; 257 struct pipe_blend_state blend; 258 259 if (!blend_for_op(&blend_opt, comp->op, comp->src, comp->mask, comp->dst)) 260 return -XA_ERR_INVAL; 261 262 memset(&blend, 0, sizeof(struct pipe_blend_state)); 263 blend.rt[0].blend_enable = 1; 264 blend.rt[0].colormask = PIPE_MASK_RGBA; 265 266 blend.rt[0].rgb_src_factor = blend_opt.rgb_src; 267 blend.rt[0].alpha_src_factor = blend_opt.rgb_src; 268 blend.rt[0].rgb_dst_factor = blend_opt.rgb_dst; 269 blend.rt[0].alpha_dst_factor = blend_opt.rgb_dst; 270 271 cso_set_blend(ctx->cso, &blend); 272 return XA_ERR_NONE; 273} 274 275static unsigned int 276picture_format_fixups(struct xa_picture *src_pic, 277 int mask) 278{ 279 boolean set_alpha = FALSE; 280 boolean swizzle = FALSE; 281 unsigned ret = 0; 282 struct xa_surface *src = src_pic->srf; 283 enum xa_formats src_hw_format, src_pic_format; 284 enum xa_surface_type src_hw_type, src_pic_type; 285 286 if (!src) 287 return 0; 288 289 src_hw_format = xa_surface_format(src); 290 src_pic_format = src_pic->pict_format; 291 292 set_alpha = (xa_format_type_is_color(src_hw_format) && 293 xa_format_a(src_pic_format) == 0); 294 295 if (set_alpha) 296 ret |= mask ? FS_MASK_SET_ALPHA : FS_SRC_SET_ALPHA; 297 298 if (src_hw_format == src_pic_format) { 299 if (src->tex->format == PIPE_FORMAT_L8_UNORM || 300 src->tex->format == PIPE_FORMAT_R8_UNORM) 301 return ((mask) ? FS_MASK_LUMINANCE : FS_SRC_LUMINANCE); 302 303 return ret; 304 } 305 306 src_hw_type = xa_format_type(src_hw_format); 307 src_pic_type = xa_format_type(src_pic_format); 308 309 swizzle = ((src_hw_type == xa_type_argb && 310 src_pic_type == xa_type_abgr) || 311 ((src_hw_type == xa_type_abgr && 312 src_pic_type == xa_type_argb))); 313 314 if (!swizzle && (src_hw_type != src_pic_type)) 315 return ret; 316 317 if (swizzle) 318 ret |= mask ? FS_MASK_SWIZZLE_RGB : FS_SRC_SWIZZLE_RGB; 319 320 return ret; 321} 322 323static void 324xa_src_in_mask(float src[4], const float mask[4]) 325{ 326 src[0] *= mask[3]; 327 src[1] *= mask[3]; 328 src[2] *= mask[3]; 329 src[3] *= mask[3]; 330} 331 332/** 333 * xa_handle_src_pict - Set up xa_context state and fragment shader 334 * input based on scr_pict type 335 * 336 * \param ctx[in, out]: Pointer to the xa context. 337 * \param src_pict[in]: Pointer to the union xa_source_pict to consider. 338 * \param is_mask[in]: Whether we're considering a mask picture. 339 * 340 * \returns TRUE if succesful, FALSE otherwise. 341 * 342 * This function computes some xa_context state used to determine whether 343 * to upload the solid color and also the solid color itself used as an input 344 * to the fragment shader. 345 */ 346static boolean 347xa_handle_src_pict(struct xa_context *ctx, 348 const union xa_source_pict *src_pict, 349 boolean is_mask) 350{ 351 float solid_color[4]; 352 353 switch(src_pict->type) { 354 case xa_src_pict_solid_fill: 355 xa_pixel_to_float4(src_pict->solid_fill.color, solid_color); 356 break; 357 case xa_src_pict_float_solid_fill: 358 memcpy(solid_color, src_pict->float_solid_fill.color, 359 sizeof(solid_color)); 360 break; 361 default: 362 return FALSE; 363 } 364 365 if (is_mask && ctx->has_solid_src) 366 xa_src_in_mask(ctx->solid_color, solid_color); 367 else 368 memcpy(ctx->solid_color, solid_color, sizeof(solid_color)); 369 370 if (is_mask) 371 ctx->has_solid_mask = TRUE; 372 else 373 ctx->has_solid_src = TRUE; 374 375 return TRUE; 376} 377 378static int 379bind_shaders(struct xa_context *ctx, const struct xa_composite *comp) 380{ 381 unsigned vs_traits = 0, fs_traits = 0; 382 struct xa_shader shader; 383 struct xa_picture *src_pic = comp->src; 384 struct xa_picture *mask_pic = comp->mask; 385 struct xa_picture *dst_pic = comp->dst; 386 387 ctx->has_solid_src = FALSE; 388 ctx->has_solid_mask = FALSE; 389 390 if (dst_pic && xa_format_type(dst_pic->pict_format) != 391 xa_format_type(xa_surface_format(dst_pic->srf))) 392 return -XA_ERR_INVAL; 393 394 if (src_pic) { 395 if (src_pic->wrap == xa_wrap_clamp_to_border && src_pic->has_transform) 396 fs_traits |= FS_SRC_REPEAT_NONE; 397 398 fs_traits |= FS_COMPOSITE; 399 vs_traits |= VS_COMPOSITE; 400 401 if (src_pic->src_pict) { 402 if (!xa_handle_src_pict(ctx, src_pic->src_pict, false)) 403 return -XA_ERR_INVAL; 404 fs_traits |= FS_SRC_SRC; 405 vs_traits |= VS_SRC_SRC; 406 } else 407 fs_traits |= picture_format_fixups(src_pic, 0); 408 } 409 410 if (mask_pic) { 411 vs_traits |= VS_MASK; 412 fs_traits |= FS_MASK; 413 if (mask_pic->component_alpha) 414 fs_traits |= FS_CA; 415 if (mask_pic->src_pict) { 416 if (!xa_handle_src_pict(ctx, mask_pic->src_pict, true)) 417 return -XA_ERR_INVAL; 418 419 if (ctx->has_solid_src) { 420 vs_traits &= ~VS_MASK; 421 fs_traits &= ~FS_MASK; 422 } else { 423 vs_traits |= VS_MASK_SRC; 424 fs_traits |= FS_MASK_SRC; 425 } 426 } else { 427 if (mask_pic->wrap == xa_wrap_clamp_to_border && 428 mask_pic->has_transform) 429 fs_traits |= FS_MASK_REPEAT_NONE; 430 431 fs_traits |= picture_format_fixups(mask_pic, 1); 432 } 433 } 434 435 if (ctx->srf->format == PIPE_FORMAT_L8_UNORM || 436 ctx->srf->format == PIPE_FORMAT_R8_UNORM) 437 fs_traits |= FS_DST_LUMINANCE; 438 439 shader = xa_shaders_get(ctx->shaders, vs_traits, fs_traits); 440 cso_set_vertex_shader_handle(ctx->cso, shader.vs); 441 cso_set_fragment_shader_handle(ctx->cso, shader.fs); 442 return XA_ERR_NONE; 443} 444 445static void 446bind_samplers(struct xa_context *ctx, 447 const struct xa_composite *comp) 448{ 449 struct pipe_sampler_state *samplers[PIPE_MAX_SAMPLERS]; 450 struct pipe_sampler_state src_sampler, mask_sampler; 451 struct pipe_sampler_view view_templ; 452 struct pipe_sampler_view *src_view; 453 struct pipe_context *pipe = ctx->pipe; 454 struct xa_picture *src_pic = comp->src; 455 struct xa_picture *mask_pic = comp->mask; 456 int num_samplers = 0; 457 458 xa_ctx_sampler_views_destroy(ctx); 459 memset(&src_sampler, 0, sizeof(struct pipe_sampler_state)); 460 memset(&mask_sampler, 0, sizeof(struct pipe_sampler_state)); 461 462 if (src_pic && !ctx->has_solid_src) { 463 unsigned src_wrap = xa_repeat_to_gallium(src_pic->wrap); 464 int filter; 465 466 (void) xa_filter_to_gallium(src_pic->filter, &filter); 467 468 src_sampler.wrap_s = src_wrap; 469 src_sampler.wrap_t = src_wrap; 470 src_sampler.min_img_filter = filter; 471 src_sampler.mag_img_filter = filter; 472 src_sampler.min_mip_filter = PIPE_TEX_MIPFILTER_NEAREST; 473 src_sampler.normalized_coords = 1; 474 samplers[0] = &src_sampler; 475 u_sampler_view_default_template(&view_templ, 476 src_pic->srf->tex,+ src_pic->srf->tex->format); 477 src_view = pipe->create_sampler_view(pipe, src_pic->srf->tex, 478 &view_templ); 479 ctx->bound_sampler_views[0] = src_view; 480 num_samplers++; 481 } 482 483 if (mask_pic && !ctx->has_solid_mask) { 484 unsigned mask_wrap = xa_repeat_to_gallium(mask_pic->wrap); 485 int filter; 486 487 (void) xa_filter_to_gallium(mask_pic->filter, &filter); 488 489 mask_sampler.wrap_s = mask_wrap; 490 mask_sampler.wrap_t = mask_wrap; 491 mask_sampler.min_img_filter = filter; 492 mask_sampler.mag_img_filter = filter; 493 src_sampler.min_mip_filter = PIPE_TEX_MIPFILTER_NEAREST; 494 mask_sampler.normalized_coords = 1; 495 samplers[num_samplers] = &mask_sampler; 496 u_sampler_view_default_template(&view_templ, 497 mask_pic->srf->tex, 498 mask_pic->srf->tex->format); 499 src_view = pipe->create_sampler_view(pipe, mask_pic->srf->tex, 500 &view_templ); 501 ctx->bound_sampler_views[num_samplers] = src_view; 502 num_samplers++; 503 } 504 505 cso_set_samplers(ctx->cso, PIPE_SHADER_FRAGMENT, num_samplers, 506 (const struct pipe_sampler_state **)samplers); 507 pipe->set_sampler_views(pipe, PIPE_SHADER_FRAGMENT, 0, num_samplers, 0, 508 false, ctx->bound_sampler_views); 509 ctx->num_bound_samplers = num_samplers; 510} 511 512XA_EXPORT int 513xa_composite_prepare(struct xa_context *ctx, 514 const struct xa_composite *comp) 515{ 516 struct xa_surface *dst_srf = comp->dst->srf; 517 int ret; 518 519 ret = xa_ctx_srf_create(ctx, dst_srf); 520 if (ret != XA_ERR_NONE) 521 return ret; 522 523 ctx->dst = dst_srf; 524 renderer_bind_destination(ctx, ctx->srf); 525 526 ret = bind_composite_blend_state(ctx, comp); 527 if (ret != XA_ERR_NONE) 528 return ret; 529 ret = bind_shaders(ctx, comp); 530 if (ret != XA_ERR_NONE) 531 return ret; 532 bind_samplers(ctx, comp); 533 534 if (ctx->num_bound_samplers == 0 ) { /* solid fill */ 535 renderer_begin_solid(ctx); 536 } else { 537 renderer_begin_textures(ctx); 538 ctx->comp = comp; 539 } 540 541 xa_ctx_srf_destroy(ctx); 542 return XA_ERR_NONE; 543} 544 545XA_EXPORT void 546xa_composite_rect(struct xa_context *ctx, 547 int srcX, int srcY, int maskX, int maskY, 548 int dstX, int dstY, int width, int height) 549{ 550 if (ctx->num_bound_samplers == 0 ) { /* solid fill */ 551 xa_scissor_update(ctx, dstX, dstY, dstX + width, dstY + height); 552 renderer_solid(ctx, dstX, dstY, dstX + width, dstY + height); 553 } else { 554 const struct xa_composite *comp = ctx->comp; 555 int pos[6] = {srcX, srcY, maskX, maskY, dstX, dstY}; 556 const float *src_matrix = NULL; 557 const float *mask_matrix = NULL; 558 559 xa_scissor_update(ctx, dstX, dstY, dstX + width, dstY + height); 560 561 if (comp->src->has_transform) 562 src_matrix = comp->src->transform; 563 if (comp->mask && comp->mask->has_transform) 564 mask_matrix = comp->mask->transform; 565 566 renderer_texture(ctx, pos, width, height, 567 src_matrix, mask_matrix); 568 } 569} 570 571XA_EXPORT void 572xa_composite_done(struct xa_context *ctx) 573{ 574 renderer_draw_flush(ctx); 575 576 ctx->comp = NULL; 577 ctx->has_solid_src = FALSE; 578 ctx->has_solid_mask = FALSE; 579 xa_ctx_sampler_views_destroy(ctx); 580} 581 582static const struct xa_composite_allocation a = { 583 .xa_composite_size = sizeof(struct xa_composite), 584 .xa_picture_size = sizeof(struct xa_picture), 585 .xa_source_pict_size = sizeof(union xa_source_pict), 586}; 587 588XA_EXPORT const struct xa_composite_allocation * 589xa_composite_allocation(void) 590{ 591 return &a; 592} 593