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 41 struct 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 52 static 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 */ 88 static unsigned 89 xa_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 102 static boolean 103 blend_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 * Temporarily disable component alpha since it appears buggy. 116 */ 117 if (mask_pic && mask_pic->component_alpha) 118 return FALSE; 119 120 /* 121 * our default in case something goes wrong 122 */ 123 *blend = xa_blends[XA_BLEND_OP_OVER]; 124 125 for (i = 0; i < num_blends; ++i) { 126 if (xa_blends[i].op == op) { 127 *blend = xa_blends[i]; 128 supported = TRUE; 129 } 130 } 131 132 if (!dst_pic->srf) 133 return supported; 134 135 if ((dst_pic->srf->tex->format == PIPE_FORMAT_L8_UNORM || 136 dst_pic->srf->tex->format == PIPE_FORMAT_R8_UNORM)) { 137 blend->rgb_src = xa_convert_blend_for_luminance(blend->rgb_src); 138 blend->rgb_dst = xa_convert_blend_for_luminance(blend->rgb_dst); 139 } 140 141 /* 142 * If there's no dst alpha channel, adjust the blend op so that we'll treat 143 * it as always 1. 144 */ 145 146 if (xa_format_a(dst_pic->pict_format) == 0 && blend->alpha_dst) { 147 if (blend->rgb_src == PIPE_BLENDFACTOR_DST_ALPHA) 148 blend->rgb_src = PIPE_BLENDFACTOR_ONE; 149 else if (blend->rgb_src == PIPE_BLENDFACTOR_INV_DST_ALPHA) 150 blend->rgb_src = PIPE_BLENDFACTOR_ZERO; 151 } 152 153 /* 154 * If the source alpha is being used, then we should only be in a case where 155 * the source blend factor is 0, and the source blend value is the mask 156 * channels multiplied by the source picture's alpha. 157 */ 158 if (mask_pic && mask_pic->component_alpha && 159 xa_format_rgb(mask_pic->pict_format) && 160 blend->alpha_src) { 161 if (blend->rgb_dst == PIPE_BLENDFACTOR_SRC_ALPHA) { 162 blend->rgb_dst = PIPE_BLENDFACTOR_SRC_COLOR; 163 } else if (blend->rgb_dst == PIPE_BLENDFACTOR_INV_SRC_ALPHA) { 164 blend->rgb_dst = PIPE_BLENDFACTOR_INV_SRC_COLOR; 165 } 166 } 167 168 return supported; 169 } 170 171 172 static inline int 173 xa_repeat_to_gallium(int mode) 174 { 175 switch(mode) { 176 case xa_wrap_clamp_to_border: 177 return PIPE_TEX_WRAP_CLAMP_TO_BORDER; 178 case xa_wrap_repeat: 179 return PIPE_TEX_WRAP_REPEAT; 180 case xa_wrap_mirror_repeat: 181 return PIPE_TEX_WRAP_MIRROR_REPEAT; 182 case xa_wrap_clamp_to_edge: 183 return PIPE_TEX_WRAP_CLAMP_TO_EDGE; 184 default: 185 break; 186 } 187 return PIPE_TEX_WRAP_REPEAT; 188 } 189 190 static inline boolean 191 xa_filter_to_gallium(int xrender_filter, int *out_filter) 192 { 193 194 switch (xrender_filter) { 195 case xa_filter_nearest: 196 *out_filter = PIPE_TEX_FILTER_NEAREST; 197 break; 198 case xa_filter_linear: 199 *out_filter = PIPE_TEX_FILTER_LINEAR; 200 break; 201 default: 202 *out_filter = PIPE_TEX_FILTER_NEAREST; 203 return FALSE; 204 } 205 return TRUE; 206 } 207 208 static int 209 xa_is_filter_accelerated(struct xa_picture *pic) 210 { 211 int filter; 212 if (pic && !xa_filter_to_gallium(pic->filter, &filter)) 213 return 0; 214 return 1; 215 } 216 217 XA_EXPORT int 218 xa_composite_check_accelerated(const struct xa_composite *comp) 219 { 220 struct xa_composite_blend blend; 221 struct xa_picture *src_pic = comp->src; 222 223 if (!xa_is_filter_accelerated(src_pic) || 224 !xa_is_filter_accelerated(comp->mask)) { 225 return -XA_ERR_INVAL; 226 } 227 228 229 if (src_pic->src_pict) { 230 if (src_pic->src_pict->type != xa_src_pict_solid_fill) 231 return -XA_ERR_INVAL; 232 233 /* 234 * Currently we don't support solid fill with a mask. 235 * We can easily do that, but that would require shader, 236 * sampler view setup and vertex setup modification. 237 */ 238 if (comp->mask) 239 return -XA_ERR_INVAL; 240 } 241 242 if (blend_for_op(&blend, comp->op, comp->src, comp->mask, comp->dst)) { 243 struct xa_picture *mask = comp->mask; 244 if (mask && mask->component_alpha && 245 xa_format_rgb(mask->pict_format)) { 246 if (blend.alpha_src && blend.rgb_src != PIPE_BLENDFACTOR_ZERO) { 247 return -XA_ERR_INVAL; 248 } 249 } 250 251 return XA_ERR_NONE; 252 } 253 return -XA_ERR_INVAL; 254 } 255 256 static int 257 bind_composite_blend_state(struct xa_context *ctx, 258 const struct xa_composite *comp) 259 { 260 struct xa_composite_blend blend_opt; 261 struct pipe_blend_state blend; 262 263 if (!blend_for_op(&blend_opt, comp->op, comp->src, comp->mask, comp->dst)) 264 return -XA_ERR_INVAL; 265 266 memset(&blend, 0, sizeof(struct pipe_blend_state)); 267 blend.rt[0].blend_enable = 1; 268 blend.rt[0].colormask = PIPE_MASK_RGBA; 269 270 blend.rt[0].rgb_src_factor = blend_opt.rgb_src; 271 blend.rt[0].alpha_src_factor = blend_opt.rgb_src; 272 blend.rt[0].rgb_dst_factor = blend_opt.rgb_dst; 273 blend.rt[0].alpha_dst_factor = blend_opt.rgb_dst; 274 275 cso_set_blend(ctx->cso, &blend); 276 return XA_ERR_NONE; 277 } 278 279 static unsigned int 280 picture_format_fixups(struct xa_picture *src_pic, 281 int mask) 282 { 283 boolean set_alpha = FALSE; 284 boolean swizzle = FALSE; 285 unsigned ret = 0; 286 struct xa_surface *src = src_pic->srf; 287 enum xa_formats src_hw_format, src_pic_format; 288 enum xa_surface_type src_hw_type, src_pic_type; 289 290 if (!src) 291 return 0; 292 293 src_hw_format = xa_surface_format(src); 294 src_pic_format = src_pic->pict_format; 295 296 set_alpha = (xa_format_type_is_color(src_pic_format) && 297 xa_format_a(src_pic_format) == 0); 298 299 if (set_alpha) 300 ret |= mask ? FS_MASK_SET_ALPHA : FS_SRC_SET_ALPHA; 301 302 if (src_hw_format == src_pic_format) { 303 if (src->tex->format == PIPE_FORMAT_L8_UNORM || 304 src->tex->format == PIPE_FORMAT_R8_UNORM) 305 return ((mask) ? FS_MASK_LUMINANCE : FS_SRC_LUMINANCE); 306 307 return ret; 308 } 309 310 src_hw_type = xa_format_type(src_hw_format); 311 src_pic_type = xa_format_type(src_pic_format); 312 313 swizzle = ((src_hw_type == xa_type_argb && 314 src_pic_type == xa_type_abgr) || 315 ((src_hw_type == xa_type_abgr && 316 src_pic_type == xa_type_argb))); 317 318 if (!swizzle && (src_hw_type != src_pic_type)) 319 return ret; 320 321 if (swizzle) 322 ret |= mask ? FS_MASK_SWIZZLE_RGB : FS_SRC_SWIZZLE_RGB; 323 324 return ret; 325 } 326 327 static int 328 bind_shaders(struct xa_context *ctx, const struct xa_composite *comp) 329 { 330 unsigned vs_traits = 0, fs_traits = 0; 331 struct xa_shader shader; 332 struct xa_picture *src_pic = comp->src; 333 struct xa_picture *mask_pic = comp->mask; 334 335 ctx->has_solid_color = FALSE; 336 337 if (src_pic) { 338 if (src_pic->wrap == xa_wrap_clamp_to_border && src_pic->has_transform) 339 fs_traits |= FS_SRC_REPEAT_NONE; 340 341 if (src_pic->src_pict) { 342 if (src_pic->src_pict->type == xa_src_pict_solid_fill) { 343 fs_traits |= FS_SOLID_FILL | FS_FILL; 344 vs_traits |= VS_SOLID_FILL; 345 xa_pixel_to_float4(src_pic->src_pict->solid_fill.color, 346 ctx->solid_color); 347 ctx->has_solid_color = TRUE; 348 } 349 } else { 350 fs_traits |= FS_COMPOSITE; 351 vs_traits |= VS_COMPOSITE; 352 } 353 354 fs_traits |= picture_format_fixups(src_pic, 0); 355 } 356 357 if (mask_pic) { 358 vs_traits |= VS_MASK; 359 fs_traits |= FS_MASK; 360 if (mask_pic->wrap == xa_wrap_clamp_to_border && 361 mask_pic->has_transform) 362 fs_traits |= FS_MASK_REPEAT_NONE; 363 364 if (mask_pic->component_alpha) { 365 struct xa_composite_blend blend; 366 if (!blend_for_op(&blend, comp->op, src_pic, mask_pic, NULL)) 367 return -XA_ERR_INVAL; 368 369 if (blend.alpha_src) { 370 fs_traits |= FS_CA_SRCALPHA; 371 } else 372 fs_traits |= FS_CA_FULL; 373 } 374 375 fs_traits |= picture_format_fixups(mask_pic, 1); 376 } 377 378 if (ctx->srf->format == PIPE_FORMAT_L8_UNORM || 379 ctx->srf->format == PIPE_FORMAT_R8_UNORM) 380 fs_traits |= FS_DST_LUMINANCE; 381 382 shader = xa_shaders_get(ctx->shaders, vs_traits, fs_traits); 383 cso_set_vertex_shader_handle(ctx->cso, shader.vs); 384 cso_set_fragment_shader_handle(ctx->cso, shader.fs); 385 return XA_ERR_NONE; 386 } 387 388 static void 389 bind_samplers(struct xa_context *ctx, 390 const struct xa_composite *comp) 391 { 392 struct pipe_sampler_state *samplers[PIPE_MAX_SAMPLERS]; 393 struct pipe_sampler_state src_sampler, mask_sampler; 394 struct pipe_sampler_view view_templ; 395 struct pipe_sampler_view *src_view; 396 struct pipe_context *pipe = ctx->pipe; 397 struct xa_picture *src_pic = comp->src; 398 struct xa_picture *mask_pic = comp->mask; 399 400 ctx->num_bound_samplers = 0; 401 402 memset(&src_sampler, 0, sizeof(struct pipe_sampler_state)); 403 memset(&mask_sampler, 0, sizeof(struct pipe_sampler_state)); 404 405 if (src_pic) { 406 if (ctx->has_solid_color) { 407 samplers[0] = NULL; 408 pipe_sampler_view_reference(&ctx->bound_sampler_views[0], NULL); 409 } else { 410 unsigned src_wrap = xa_repeat_to_gallium(src_pic->wrap); 411 int filter; 412 413 (void) xa_filter_to_gallium(src_pic->filter, &filter); 414 415 src_sampler.wrap_s = src_wrap; 416 src_sampler.wrap_t = src_wrap; 417 src_sampler.min_img_filter = filter; 418 src_sampler.mag_img_filter = filter; 419 src_sampler.min_mip_filter = PIPE_TEX_MIPFILTER_NEAREST; 420 src_sampler.normalized_coords = 1; 421 samplers[0] = &src_sampler; 422 ctx->num_bound_samplers = 1; 423 u_sampler_view_default_template(&view_templ, 424 src_pic->srf->tex, 425 src_pic->srf->tex->format); 426 src_view = pipe->create_sampler_view(pipe, src_pic->srf->tex, 427 &view_templ); 428 pipe_sampler_view_reference(&ctx->bound_sampler_views[0], NULL); 429 ctx->bound_sampler_views[0] = src_view; 430 } 431 } 432 433 if (mask_pic) { 434 unsigned mask_wrap = xa_repeat_to_gallium(mask_pic->wrap); 435 int filter; 436 437 (void) xa_filter_to_gallium(mask_pic->filter, &filter); 438 439 mask_sampler.wrap_s = mask_wrap; 440 mask_sampler.wrap_t = mask_wrap; 441 mask_sampler.min_img_filter = filter; 442 mask_sampler.mag_img_filter = filter; 443 src_sampler.min_mip_filter = PIPE_TEX_MIPFILTER_NEAREST; 444 mask_sampler.normalized_coords = 1; 445 samplers[1] = &mask_sampler; 446 ctx->num_bound_samplers = 2; 447 u_sampler_view_default_template(&view_templ, 448 mask_pic->srf->tex, 449 mask_pic->srf->tex->format); 450 src_view = pipe->create_sampler_view(pipe, mask_pic->srf->tex, 451 &view_templ); 452 pipe_sampler_view_reference(&ctx->bound_sampler_views[1], NULL); 453 ctx->bound_sampler_views[1] = src_view; 454 455 456 /* 457 * If src is a solid color, we have no src view, so set up a 458 * dummy one that will not be used anyway. 459 */ 460 if (ctx->bound_sampler_views[0] == NULL) 461 pipe_sampler_view_reference(&ctx->bound_sampler_views[0], 462 src_view); 463 464 } 465 466 cso_set_samplers(ctx->cso, PIPE_SHADER_FRAGMENT, ctx->num_bound_samplers, 467 (const struct pipe_sampler_state **)samplers); 468 cso_set_sampler_views(ctx->cso, PIPE_SHADER_FRAGMENT, ctx->num_bound_samplers, 469 ctx->bound_sampler_views); 470 } 471 472 XA_EXPORT int 473 xa_composite_prepare(struct xa_context *ctx, 474 const struct xa_composite *comp) 475 { 476 struct xa_surface *dst_srf = comp->dst->srf; 477 int ret; 478 479 if (comp->mask && !comp->mask->srf) 480 return -XA_ERR_INVAL; 481 482 ret = xa_ctx_srf_create(ctx, dst_srf); 483 if (ret != XA_ERR_NONE) 484 return ret; 485 486 ctx->dst = dst_srf; 487 renderer_bind_destination(ctx, ctx->srf); 488 489 ret = bind_composite_blend_state(ctx, comp); 490 if (ret != XA_ERR_NONE) 491 return ret; 492 ret = bind_shaders(ctx, comp); 493 if (ret != XA_ERR_NONE) 494 return ret; 495 bind_samplers(ctx, comp); 496 497 if (ctx->num_bound_samplers == 0 ) { /* solid fill */ 498 renderer_begin_solid(ctx); 499 } else { 500 renderer_begin_textures(ctx); 501 ctx->comp = comp; 502 } 503 504 xa_ctx_srf_destroy(ctx); 505 return XA_ERR_NONE; 506 } 507 508 XA_EXPORT void 509 xa_composite_rect(struct xa_context *ctx, 510 int srcX, int srcY, int maskX, int maskY, 511 int dstX, int dstY, int width, int height) 512 { 513 if (ctx->num_bound_samplers == 0 ) { /* solid fill */ 514 renderer_solid(ctx, dstX, dstY, dstX + width, dstY + height, 515 ctx->solid_color); 516 } else { 517 const struct xa_composite *comp = ctx->comp; 518 int pos[6] = {srcX, srcY, maskX, maskY, dstX, dstY}; 519 const float *src_matrix = NULL; 520 const float *mask_matrix = NULL; 521 522 xa_scissor_update(ctx, dstX, dstY, dstX + width, dstY + height); 523 524 if (comp->src->has_transform) 525 src_matrix = comp->src->transform; 526 if (comp->mask && comp->mask->has_transform) 527 mask_matrix = comp->mask->transform; 528 529 renderer_texture(ctx, pos, width, height, 530 src_matrix, mask_matrix); 531 } 532 } 533 534 XA_EXPORT void 535 xa_composite_done(struct xa_context *ctx) 536 { 537 renderer_draw_flush(ctx); 538 539 ctx->comp = NULL; 540 ctx->has_solid_color = FALSE; 541 xa_ctx_sampler_views_destroy(ctx); 542 } 543 544 static const struct xa_composite_allocation a = { 545 .xa_composite_size = sizeof(struct xa_composite), 546 .xa_picture_size = sizeof(struct xa_picture), 547 .xa_source_pict_size = sizeof(union xa_source_pict), 548 }; 549 550 XA_EXPORT const struct xa_composite_allocation * 551 xa_composite_allocation(void) 552 { 553 return &a; 554 } 555