diff --git a/src/sna/blt.c b/src/sna/blt.c index dc4e3e18..c534f754 100644 --- a/src/sna/blt.c +++ b/src/sna/blt.c @@ -1240,3 +1240,241 @@ memcpy_xor(const void *src, void *dst, int bpp, } } } + +#define BILINEAR_INTERPOLATION_BITS 4 +static force_inline int +bilinear_weight(pixman_fixed_t x) +{ + return (x >> (16 - BILINEAR_INTERPOLATION_BITS)) & + ((1 << BILINEAR_INTERPOLATION_BITS) - 1); +} + +#if BILINEAR_INTERPOLATION_BITS <= 4 +/* Inspired by Filter_32_opaque from Skia */ +static force_inline uint32_t +bilinear_interpolation(uint32_t tl, uint32_t tr, + uint32_t bl, uint32_t br, + int distx, int disty) +{ + int distxy, distxiy, distixy, distixiy; + uint32_t lo, hi; + + distx <<= (4 - BILINEAR_INTERPOLATION_BITS); + disty <<= (4 - BILINEAR_INTERPOLATION_BITS); + + distxy = distx * disty; + distxiy = (distx << 4) - distxy; /* distx * (16 - disty) */ + distixy = (disty << 4) - distxy; /* disty * (16 - distx) */ + distixiy = + 16 * 16 - (disty << 4) - + (distx << 4) + distxy; /* (16 - distx) * (16 - disty) */ + + lo = (tl & 0xff00ff) * distixiy; + hi = ((tl >> 8) & 0xff00ff) * distixiy; + + lo += (tr & 0xff00ff) * distxiy; + hi += ((tr >> 8) & 0xff00ff) * distxiy; + + lo += (bl & 0xff00ff) * distixy; + hi += ((bl >> 8) & 0xff00ff) * distixy; + + lo += (br & 0xff00ff) * distxy; + hi += ((br >> 8) & 0xff00ff) * distxy; + + return ((lo >> 8) & 0xff00ff) | (hi & ~0xff00ff); +} +#elif SIZEOF_LONG > 4 +static force_inline uint32_t +bilinear_interpolation(uint32_t tl, uint32_t tr, + uint32_t bl, uint32_t br, + int distx, int disty) +{ + uint64_t distxy, distxiy, distixy, distixiy; + uint64_t tl64, tr64, bl64, br64; + uint64_t f, r; + + distx <<= (8 - BILINEAR_INTERPOLATION_BITS); + disty <<= (8 - BILINEAR_INTERPOLATION_BITS); + + distxy = distx * disty; + distxiy = distx * (256 - disty); + distixy = (256 - distx) * disty; + distixiy = (256 - distx) * (256 - disty); + + /* Alpha and Blue */ + tl64 = tl & 0xff0000ff; + tr64 = tr & 0xff0000ff; + bl64 = bl & 0xff0000ff; + br64 = br & 0xff0000ff; + + f = tl64 * distixiy + tr64 * distxiy + bl64 * distixy + br64 * distxy; + r = f & 0x0000ff0000ff0000ull; + + /* Red and Green */ + tl64 = tl; + tl64 = ((tl64 << 16) & 0x000000ff00000000ull) | (tl64 & 0x0000ff00ull); + + tr64 = tr; + tr64 = ((tr64 << 16) & 0x000000ff00000000ull) | (tr64 & 0x0000ff00ull); + + bl64 = bl; + bl64 = ((bl64 << 16) & 0x000000ff00000000ull) | (bl64 & 0x0000ff00ull); + + br64 = br; + br64 = ((br64 << 16) & 0x000000ff00000000ull) | (br64 & 0x0000ff00ull); + + f = tl64 * distixiy + tr64 * distxiy + bl64 * distixy + br64 * distxy; + r |= ((f >> 16) & 0x000000ff00000000ull) | (f & 0xff000000ull); + + return (uint32_t)(r >> 16); +} +#else +static force_inline uint32_t +bilinear_interpolation(uint32_t tl, uint32_t tr, + uint32_t bl, uint32_t br, + int distx, int disty) +{ + int distxy, distxiy, distixy, distixiy; + uint32_t f, r; + + distx <<= (8 - BILINEAR_INTERPOLATION_BITS); + disty <<= (8 - BILINEAR_INTERPOLATION_BITS); + + distxy = distx * disty; + distxiy = (distx << 8) - distxy; /* distx * (256 - disty) */ + distixy = (disty << 8) - distxy; /* disty * (256 - distx) */ + distixiy = + 256 * 256 - (disty << 8) - + (distx << 8) + distxy; /* (256 - distx) * (256 - disty) */ + + /* Blue */ + r = ((tl & 0x000000ff) * distixiy + (tr & 0x000000ff) * distxiy + + (bl & 0x000000ff) * distixy + (br & 0x000000ff) * distxy); + + /* Green */ + f = ((tl & 0x0000ff00) * distixiy + (tr & 0x0000ff00) * distxiy + + (bl & 0x0000ff00) * distixy + (br & 0x0000ff00) * distxy); + r |= f & 0xff000000; + + tl >>= 16; + tr >>= 16; + bl >>= 16; + br >>= 16; + r >>= 16; + + /* Red */ + f = ((tl & 0x000000ff) * distixiy + (tr & 0x000000ff) * distxiy + + (bl & 0x000000ff) * distixy + (br & 0x000000ff) * distxy); + r |= f & 0x00ff0000; + + /* Alpha */ + f = ((tl & 0x0000ff00) * distixiy + (tr & 0x0000ff00) * distxiy + + (bl & 0x0000ff00) * distixy + (br & 0x0000ff00) * distxy); + r |= f & 0xff000000; + + return r; +} +#endif + +static inline uint32_t convert_pixel(const uint8_t *p, int x) +{ + return ((uint32_t *)p)[x]; +} + +fast void +affine_blt(const void *src, void *dst, int bpp, + int16_t src_x, int16_t src_y, + int16_t src_width, int16_t src_height, + int32_t src_stride, + int16_t dst_x, int16_t dst_y, + uint16_t dst_width, uint16_t dst_height, + int32_t dst_stride, + const struct pixman_f_transform *t) +{ + static const uint8_t zero[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + const pixman_fixed_t ux = pixman_double_to_fixed(t->m[0][0]); + const pixman_fixed_t uy = pixman_double_to_fixed(t->m[1][0]); + int i, j; + + assert(bpp == 32); + + for (j = 0; j < dst_height; j++) { + pixman_fixed_t x, y; + pixman_f_vector_t v; + uint32_t *b; + + /* reference point is the center of the pixel */ + v.v[0] = dst_x + 0.5; + v.v[1] = dst_y + j + 0.5; + v.v[2] = 1.0; + + pixman_f_transform_point_3d(t, &v); + + x = pixman_double_to_fixed(v.v[0]); + x += pixman_int_to_fixed(src_x - dst_x); + y = pixman_double_to_fixed(v.v[1]); + y += pixman_int_to_fixed(src_y - dst_y); + + b = (uint32_t*)((uint8_t *)dst + (dst_y + j) * dst_stride + dst_x * bpp / 8); + for (i = 0; i < dst_width; i++) { + const uint8_t *row1; + const uint8_t *row2; + int x1, y1, x2, y2; + uint32_t tl, tr, bl, br; + int32_t fx, fy; + + x1 = x - pixman_fixed_1/2; + y1 = y - pixman_fixed_1/2; + + fx = bilinear_weight(x1); + fy = bilinear_weight(y1); + + x1 = pixman_fixed_to_int(x1); + x2 = x1 + 1; + y1 = pixman_fixed_to_int(y1); + y2 = y1 + 1; + + if (x1 >= src_width || x2 < 0 || + y1 >= src_height || y2 < 0) { + b[i] = 0; + goto next; + } + + if (y2 == 0) { + row1 = zero; + } else { + row1 = (uint8_t *)src + src_stride * y1; + row1 += bpp / 8 * x1; + } + + if (y1 == src_height - 1) { + row2 = zero; + } else { + row2 = (uint8_t *)src + src_stride * y2; + row2 += bpp / 8 * x1; + } + + if (x2 == 0) { + tl = 0; + bl = 0; + } else { + tl = convert_pixel(row1, 0); + bl = convert_pixel(row2, 0); + } + + if (x1 == src_width - 1) { + tr = 0; + br = 0; + } else { + tr = convert_pixel(row1, 1); + br = convert_pixel(row2, 1); + } + + b[i] = bilinear_interpolation(tl, tr, bl, br, fx, fy); + +next: + x += ux; + y += uy; + } + } +} diff --git a/src/sna/sna.h b/src/sna/sna.h index 40184041..a4984849 100644 --- a/src/sna/sna.h +++ b/src/sna/sna.h @@ -1140,6 +1140,16 @@ memcpy_blt(const void *src, void *dst, int bpp, int16_t dst_x, int16_t dst_y, uint16_t width, uint16_t height); +void +affine_blt(const void *src, void *dst, int bpp, + int16_t src_x, int16_t src_y, + int16_t src_width, int16_t src_height, + int32_t src_stride, + int16_t dst_x, int16_t dst_y, + uint16_t dst_width, uint16_t dst_height, + int32_t dst_stride, + const struct pixman_f_transform *t); + void memmove_box(const void *src, void *dst, int bpp, int32_t stride, diff --git a/src/sna/sna_display.c b/src/sna/sna_display.c index a836b61e..a71edb1f 100644 --- a/src/sna/sna_display.c +++ b/src/sna/sna_display.c @@ -116,6 +116,20 @@ struct local_mode_obj_get_properties { extern XF86ConfigPtr xf86configptr; +struct sna_cursor { + struct sna_cursor *next; + uint32_t *image; + bool transformed; + Rotation rotation; + int ref; + int size; + int last_width; + int last_height; + unsigned handle; + unsigned serial; + unsigned alloc; +}; + struct sna_crtc { xf86CrtcPtr base; struct drm_mode_modeinfo kmode; @@ -128,6 +142,7 @@ struct sna_crtc { bool shadow; bool fallback_shadow; bool transform; + bool cursor_transform; bool flip_pending; uint8_t id; uint8_t pipe; @@ -1582,6 +1597,7 @@ __sna_crtc_disable(struct sna *sna, struct sna_crtc *sna_crtc) sna_crtc->transform = false; } + sna_crtc->cursor_transform = false; assert(!sna_crtc->shadow); } @@ -2194,6 +2210,34 @@ out_shadow: } } +#define SCALING_EPSILON (1./256) +static double determinant(const struct pixman_f_transform *t) +{ + return t->m[0][0]*t->m[1][1] - t->m[1][0]*t->m[0][1]; +} + +static bool +affine_is_pixel_exact(const struct pixman_f_transform *t) +{ + double det; + + if (t->m[2][0] || t->m[2][1]) + return false; + + det = t->m[2][2] * determinant(t); + if (fabs (det * det - 1.0) < SCALING_EPSILON) { + if (fabs(t->m[0][1]) < SCALING_EPSILON && + fabs(t->m[1][0]) < SCALING_EPSILON) + return true; + + if (fabs(t->m[0][0]) < SCALING_EPSILON && + fabs(t->m[1][1]) < SCALING_EPSILON) + return true; + } + + return false; +} + static void sna_crtc_randr(xf86CrtcPtr crtc) { struct sna_crtc *sna_crtc = to_sna_crtc(crtc); @@ -2239,6 +2283,10 @@ static void sna_crtc_randr(xf86CrtcPtr crtc) } else crtc->transform_in_use = sna_crtc->rotation != RR_Rotate_0; + sna_crtc->cursor_transform = false; + if (crtc->transform_in_use) + sna_crtc->cursor_transform = !affine_is_pixel_exact(&f_fb_to_crtc); + crtc->crtc_to_framebuffer = crtc_to_fb; crtc->f_crtc_to_framebuffer = f_crtc_to_fb; crtc->f_framebuffer_to_crtc = f_fb_to_crtc; @@ -2361,6 +2409,7 @@ __sna_crtc_set_mode(xf86CrtcPtr crtc) struct kgem_bo *saved_bo, *bo; uint32_t saved_offset; bool saved_transform; + bool saved_cursor_transform; DBG(("%s: CRTC=%d, pipe=%d, hidden?=%d\n", __FUNCTION__, sna_crtc->id, sna_crtc->pipe, sna->mode.hidden)); @@ -2369,6 +2418,7 @@ __sna_crtc_set_mode(xf86CrtcPtr crtc) saved_bo = sna_crtc->bo; saved_transform = sna_crtc->transform; + saved_cursor_transform = sna_crtc->cursor_transform; saved_offset = sna_crtc->offset; sna_crtc->fallback_shadow = false; @@ -2414,6 +2464,12 @@ retry: /* Attach per-crtc pixmap or direct */ sna_crtc_randr(crtc); if (sna_crtc->transform) sna_crtc_damage(crtc); + if (sna_crtc->cursor && /* Reload cursor if RandR maybe changed */ + (saved_cursor_transform || + sna_crtc->cursor_transform || + sna_crtc->cursor->rotation != crtc->rotation)) + sna_crtc_disable_cursor(sna, sna_crtc); + assert(!sna->mode.hidden); sna->mode.front_active += saved_bo == NULL; sna->mode.dirty = true; @@ -2431,6 +2487,7 @@ error: if (saved_transform) sna->mode.rr_active++; sna_crtc->transform = saved_transform; + sna_crtc->cursor_transform = saved_cursor_transform; sna_crtc->bo = saved_bo; sna_mode_discover(sna); return FALSE; @@ -4621,19 +4678,6 @@ sna_mode_resize(ScrnInfoPtr scrn, int width, int height) } /* cursor handling */ -struct sna_cursor { - struct sna_cursor *next; - uint32_t *image; - Rotation rotation; - int ref; - int size; - int last_width; - int last_height; - unsigned handle; - unsigned serial; - unsigned alloc; -}; - static void rotate_coord(Rotation rotation, int size, int x_dst, int y_dst, @@ -4759,6 +4803,17 @@ static uint32_t *get_cursor_argb(CursorPtr c) #endif } +static int __cursor_size(int width, int height) +{ + int i, size; + + i = MAX(width, height); + for (size = 64; size < i; size <<= 1) + ; + + return size; +} + static struct sna_cursor *__sna_get_cursor(struct sna *sna, xf86CrtcPtr crtc) { struct sna_cursor *cursor; @@ -4766,6 +4821,9 @@ static struct sna_cursor *__sna_get_cursor(struct sna *sna, xf86CrtcPtr crtc) const uint32_t *argb; uint32_t *image; int width, height, pitch, size, x, y; + PictTransform cursor_to_fb; + struct pict_f_transform f_cursor_to_fb, f_fb_to_cursor; + bool transformed; Rotation rotation; assert(sna->cursor.ref); @@ -4790,11 +4848,15 @@ static struct sna_cursor *__sna_get_cursor(struct sna *sna, xf86CrtcPtr crtc) sna->cursor.serial, get_cursor_argb(sna->cursor.ref) != NULL)); - rotation = crtc->transform_in_use ? crtc->rotation : RR_Rotate_0; + transformed = to_sna_crtc(crtc)->cursor_transform; + rotation = !transformed && crtc->transform_in_use ? crtc->rotation : RR_Rotate_0; - if (sna->cursor.use_gtt) { /* Don't allow phys cursor sharing */ + /* Don't allow phys cursor sharing */ + if (sna->cursor.use_gtt && !transformed) { for (cursor = sna->cursor.cursors; cursor; cursor = cursor->next) { - if (cursor->serial == sna->cursor.serial && cursor->rotation == rotation) { + if (cursor->serial == sna->cursor.serial && + cursor->rotation == rotation && + !cursor->transformed) { __DBG(("%s: reusing handle=%d, serial=%d, rotation=%d, size=%d\n", __FUNCTION__, cursor->handle, cursor->serial, cursor->rotation, cursor->size)); assert(cursor->size == sna->cursor.size); @@ -4803,7 +4865,26 @@ static struct sna_cursor *__sna_get_cursor(struct sna *sna, xf86CrtcPtr crtc) } } - size = sna->cursor.size; + if (transformed) { + struct pixman_box16 box; + + box.x1 = box.y1 = 0; + box.x2 = sna->cursor.ref->bits->width; + box.y2 = sna->cursor.ref->bits->height; + + pixman_f_transform_bounds(&crtc->f_framebuffer_to_crtc, &box); + size = __cursor_size(box.x2 - box.x1, box.y2 - box.y1); + + RRTransformCompute(0, 0, + sna->cursor.ref->bits->width, + sna->cursor.ref->bits->height, + crtc->rotation, &crtc->transform, + &cursor_to_fb, + &f_cursor_to_fb, + &f_fb_to_cursor); + } else + size = sna->cursor.size; + cursor = to_sna_crtc(crtc)->cursor; if (cursor && cursor->alloc < 4*size*size) cursor = NULL; @@ -4816,7 +4897,7 @@ static struct sna_cursor *__sna_get_cursor(struct sna *sna, xf86CrtcPtr crtc) } } - width = sna->cursor.ref->bits->width; + width = sna->cursor.ref->bits->width; height = sna->cursor.ref->bits->height; source = sna->cursor.ref->bits->source; mask = sna->cursor.ref->bits->mask; @@ -4824,7 +4905,7 @@ static struct sna_cursor *__sna_get_cursor(struct sna *sna, xf86CrtcPtr crtc) pitch = BitmapBytePad(width); image = cursor->image; - if (image == NULL) { + if (image == NULL || transformed) { image = sna->cursor.scratch; cursor->last_width = cursor->last_height = size; } @@ -4855,6 +4936,19 @@ static struct sna_cursor *__sna_get_cursor(struct sna *sna, xf86CrtcPtr crtc) mask += pitch; source += pitch; } + if (transformed) { + affine_blt(image, cursor->image, 32, + 0, 0, width, height, size * 4, + 0, 0, width, height, size * 4, + &f_cursor_to_fb); + image = cursor->image; + } + } else if (transformed) { + affine_blt(argb, cursor->image, 32, + 0, 0, width, height, width * 4, + 0, 0, width, height, size * 4, + &f_cursor_to_fb); + image = cursor->image; } else memcpy_blt(argb, image, 32, width * 4, size * 4, @@ -4901,6 +4995,7 @@ static struct sna_cursor *__sna_get_cursor(struct sna *sna, xf86CrtcPtr crtc) cursor->size = size; cursor->rotation = rotation; + cursor->transformed = transformed; cursor->serial = sna->cursor.serial; cursor->last_width = width; cursor->last_height = height; @@ -5234,17 +5329,6 @@ sna_load_cursor_image(ScrnInfoPtr scrn, unsigned char *src) { } -static int __cursor_size(CursorPtr cursor) -{ - int i, size; - - i = MAX(cursor->bits->width, cursor->bits->height); - for (size = 64; size < i; size <<= 1) - ; - - return size; -} - static bool sna_cursor_preallocate(struct sna *sna) { @@ -5262,6 +5346,40 @@ sna_cursor_preallocate(struct sna *sna) return true; } +static bool +transformable_cursor(struct sna *sna, CursorPtr cursor) +{ + xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn); + int i; + + for (i = 0; i < sna->mode.num_real_crtc; i++) { + struct sna_crtc *crtc = to_sna_crtc(config->crtc[i]); + const struct pixman_f_transform *t; + struct pixman_box16 box; + int size; + + if (!crtc->cursor_transform) + continue; + + t = &crtc->base->f_framebuffer_to_crtc; + if (!sna->cursor.use_gtt || !sna->cursor.scratch) + return false; + + box.x1 = box.y1 = 0; + box.x2 = cursor->bits->width; + box.y2 = cursor->bits->height; + + if (!pixman_f_transform_bounds(t, &box)) + return false; + + size = __cursor_size(box.x2 - box.x1, box.y2 - box.y1); + if (size > sna->cursor.max_size) + return false; + } + + return true; +} + static Bool sna_use_hw_cursor(ScreenPtr screen, CursorPtr cursor) { @@ -5279,11 +5397,12 @@ sna_use_hw_cursor(ScreenPtr screen, CursorPtr cursor) sna->cursor.ref = NULL; } - if (sna->mode.rr_active) + sna->cursor.size = + __cursor_size(cursor->bits->width, cursor->bits->height); + if (sna->cursor.size > sna->cursor.max_size) return FALSE; - sna->cursor.size = __cursor_size(cursor); - if (sna->cursor.size > sna->cursor.max_size) + if (sna->mode.rr_active && !transformable_cursor(sna, cursor)) return FALSE; if (!sna_cursor_preallocate(sna)) @@ -5346,11 +5465,9 @@ sna_cursor_pre_init(struct sna *sna) DBG(("%s: cursor updates use_gtt?=%d\n", __FUNCTION__, sna->cursor.use_gtt)); - if (!sna->cursor.use_gtt) { - sna->cursor.scratch = malloc(sna->cursor.max_size * sna->cursor.max_size * 4); - if (!sna->cursor.scratch) - sna->cursor.max_size = 0; - } + sna->cursor.scratch = malloc(sna->cursor.max_size * sna->cursor.max_size * 4); + if (!sna->cursor.scratch && !sna->cursor.use_gtt) + sna->cursor.max_size = 0; sna->cursor.num_stash = -sna->mode.num_real_crtc; @@ -6011,8 +6128,8 @@ static bool sna_probe_initial_configuration(struct sna *sna) if (sna_output->num_modes == 0) continue; - width = sna_output->modes[0].hdisplay; - height= sna_output->modes[0].vdisplay; + width = sna_output->modes[0].hdisplay; + height = sna_output->modes[0].vdisplay; DBG(("%s: panel '%s' is %dx%d\n", __FUNCTION__, output->name, width, height));