Blame pixman/pixman-ssse3.c

Packit 030a23
/*
Packit 030a23
 * Copyright © 2013 Soren Sandmann Pedersen
Packit 030a23
 * Copyright © 2013 Red Hat, Inc.
Packit 030a23
 *
Packit 030a23
 * Permission is hereby granted, free of charge, to any person obtaining a
Packit 030a23
 * copy of this software and associated documentation files (the "Software"),
Packit 030a23
 * to deal in the Software without restriction, including without limitation
Packit 030a23
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
Packit 030a23
 * and/or sell copies of the Software, and to permit persons to whom the
Packit 030a23
 * Software is furnished to do so, subject to the following conditions:
Packit 030a23
 *
Packit 030a23
 * The above copyright notice and this permission notice (including the next
Packit 030a23
 * paragraph) shall be included in all copies or substantial portions of the
Packit 030a23
 * Software.
Packit 030a23
 * 
Packit 030a23
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
Packit 030a23
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
Packit 030a23
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
Packit 030a23
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
Packit 030a23
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
Packit 030a23
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
Packit 030a23
 * DEALINGS IN THE SOFTWARE.
Packit 030a23
 *
Packit 030a23
 * Author: Soren Sandmann (soren.sandmann@gmail.com)
Packit 030a23
 */
Packit 030a23
#ifdef HAVE_CONFIG_H
Packit 030a23
#include <config.h>
Packit 030a23
#endif
Packit 030a23
Packit 030a23
#include <stdlib.h>
Packit 030a23
#include <mmintrin.h>
Packit 030a23
#include <xmmintrin.h>
Packit 030a23
#include <emmintrin.h>
Packit 030a23
#include <tmmintrin.h>
Packit 030a23
#include "pixman-private.h"
Packit 030a23
#include "pixman-inlines.h"
Packit 030a23
Packit 030a23
typedef struct
Packit 030a23
{
Packit 030a23
    int		y;
Packit 030a23
    uint64_t *	buffer;
Packit 030a23
} line_t;
Packit 030a23
Packit 030a23
typedef struct
Packit 030a23
{
Packit 030a23
    line_t		lines[2];
Packit 030a23
    pixman_fixed_t	y;
Packit 030a23
    pixman_fixed_t	x;
Packit 030a23
    uint64_t		data[1];
Packit 030a23
} bilinear_info_t;
Packit 030a23
Packit 030a23
static void
Packit 030a23
ssse3_fetch_horizontal (bits_image_t *image, line_t *line,
Packit 030a23
			int y, pixman_fixed_t x, pixman_fixed_t ux, int n)
Packit 030a23
{
Packit 030a23
    uint32_t *bits = image->bits + y * image->rowstride;
Packit 030a23
    __m128i vx = _mm_set_epi16 (
Packit 030a23
	- (x + 1), x, - (x + 1), x,
Packit 030a23
	- (x + ux + 1), x + ux,  - (x + ux + 1), x + ux);
Packit 030a23
    __m128i vux = _mm_set_epi16 (
Packit 030a23
	- 2 * ux, 2 * ux, - 2 * ux, 2 * ux,
Packit 030a23
	- 2 * ux, 2 * ux, - 2 * ux, 2 * ux);
Packit 030a23
    __m128i vaddc = _mm_set_epi16 (1, 0, 1, 0, 1, 0, 1, 0);
Packit 030a23
    __m128i *b = (__m128i *)line->buffer;
Packit 030a23
    __m128i vrl0, vrl1;
Packit 030a23
Packit 030a23
    while ((n -= 2) >= 0)
Packit 030a23
    {
Packit 030a23
	__m128i vw, vr, s;
Packit 030a23
Packit 030a23
	vrl1 = _mm_loadl_epi64 (
Packit 030a23
	    (__m128i *)(bits + pixman_fixed_to_int (x + ux)));
Packit 030a23
	/* vrl1: R1, L1 */
Packit 030a23
Packit 030a23
    final_pixel:
Packit 030a23
	vrl0 = _mm_loadl_epi64 (
Packit 030a23
	    (__m128i *)(bits + pixman_fixed_to_int (x)));
Packit 030a23
	/* vrl0: R0, L0 */
Packit 030a23
Packit 030a23
	/* The weights are based on vx which is a vector of 
Packit 030a23
	 *
Packit 030a23
	 *    - (x + 1), x, - (x + 1), x,
Packit 030a23
	 *          - (x + ux + 1), x + ux, - (x + ux + 1), x + ux
Packit 030a23
	 *
Packit 030a23
	 * so the 16 bit weights end up like this:
Packit 030a23
	 *
Packit 030a23
	 *    iw0, w0, iw0, w0, iw1, w1, iw1, w1
Packit 030a23
	 *
Packit 030a23
	 * and after shifting and packing, we get these bytes:
Packit 030a23
	 *
Packit 030a23
	 *    iw0, w0, iw0, w0, iw1, w1, iw1, w1,
Packit 030a23
	 *        iw0, w0, iw0, w0, iw1, w1, iw1, w1,
Packit 030a23
	 *
Packit 030a23
	 * which means the first and the second input pixel 
Packit 030a23
	 * have to be interleaved like this:
Packit 030a23
	 *
Packit 030a23
	 *    la0, ra0, lr0, rr0, la1, ra1, lr1, rr1,
Packit 030a23
	 *        lg0, rg0, lb0, rb0, lg1, rg1, lb1, rb1
Packit 030a23
	 *
Packit 030a23
	 * before maddubsw can be used.
Packit 030a23
	 */
Packit 030a23
Packit 030a23
	vw = _mm_add_epi16 (
Packit 030a23
	    vaddc, _mm_srli_epi16 (vx, 16 - BILINEAR_INTERPOLATION_BITS));
Packit 030a23
	/* vw: iw0, w0, iw0, w0, iw1, w1, iw1, w1
Packit 030a23
	 */
Packit 030a23
Packit 030a23
	vw = _mm_packus_epi16 (vw, vw);
Packit 030a23
	/* vw: iw0, w0, iw0, w0, iw1, w1, iw1, w1,
Packit 030a23
	 *         iw0, w0, iw0, w0, iw1, w1, iw1, w1
Packit 030a23
	 */
Packit 030a23
	vx = _mm_add_epi16 (vx, vux);
Packit 030a23
Packit 030a23
	x += 2 * ux;
Packit 030a23
Packit 030a23
	vr = _mm_unpacklo_epi16 (vrl1, vrl0);
Packit 030a23
	/* vr: rar0, rar1, rgb0, rgb1, lar0, lar1, lgb0, lgb1 */
Packit 030a23
Packit 030a23
	s = _mm_shuffle_epi32 (vr, _MM_SHUFFLE (1, 0, 3, 2));
Packit 030a23
	/* s:  lar0, lar1, lgb0, lgb1, rar0, rar1, rgb0, rgb1 */
Packit 030a23
Packit 030a23
	vr = _mm_unpackhi_epi8 (vr, s);
Packit 030a23
	/* vr: la0, ra0, lr0, rr0, la1, ra1, lr1, rr1,
Packit 030a23
	 *         lg0, rg0, lb0, rb0, lg1, rg1, lb1, rb1
Packit 030a23
	 */
Packit 030a23
Packit 030a23
	vr = _mm_maddubs_epi16 (vr, vw);
Packit 030a23
Packit 030a23
	/* When the weight is 0, the inverse weight is
Packit 030a23
	 * 128 which can't be represented in a signed byte.
Packit 030a23
	 * As a result maddubsw computes the following:
Packit 030a23
	 *
Packit 030a23
	 *     r = l * -128 + r * 0
Packit 030a23
	 *
Packit 030a23
	 * rather than the desired
Packit 030a23
	 *
Packit 030a23
	 *     r = l * 128 + r * 0
Packit 030a23
	 *
Packit 030a23
	 * We fix this by taking the absolute value of the
Packit 030a23
	 * result.
Packit 030a23
	 */
Packit 030a23
	vr = _mm_abs_epi16 (vr);
Packit 030a23
Packit 030a23
	/* vr: A0, R0, A1, R1, G0, B0, G1, B1 */
Packit 030a23
	_mm_store_si128 (b++, vr);
Packit 030a23
    }
Packit 030a23
Packit 030a23
    if (n == -1)
Packit 030a23
    {
Packit 030a23
	vrl1 = _mm_setzero_si128();
Packit 030a23
	goto final_pixel;
Packit 030a23
    }
Packit 030a23
Packit 030a23
    line->y = y;
Packit 030a23
}
Packit 030a23
Packit 030a23
static uint32_t *
Packit 030a23
ssse3_fetch_bilinear_cover (pixman_iter_t *iter, const uint32_t *mask)
Packit 030a23
{
Packit 030a23
    pixman_fixed_t fx, ux;
Packit 030a23
    bilinear_info_t *info = iter->data;
Packit 030a23
    line_t *line0, *line1;
Packit 030a23
    int y0, y1;
Packit 030a23
    int32_t dist_y;
Packit 030a23
    __m128i vw;
Packit 030a23
    int i;
Packit 030a23
Packit 030a23
    fx = info->x;
Packit 030a23
    ux = iter->image->common.transform->matrix[0][0];
Packit 030a23
Packit 030a23
    y0 = pixman_fixed_to_int (info->y);
Packit 030a23
    y1 = y0 + 1;
Packit 030a23
Packit 030a23
    line0 = &info->lines[y0 & 0x01];
Packit 030a23
    line1 = &info->lines[y1 & 0x01];
Packit 030a23
Packit 030a23
    if (line0->y != y0)
Packit 030a23
    {
Packit 030a23
	ssse3_fetch_horizontal (
Packit 030a23
	    &iter->image->bits, line0, y0, fx, ux, iter->width);
Packit 030a23
    }
Packit 030a23
Packit 030a23
    if (line1->y != y1)
Packit 030a23
    {
Packit 030a23
	ssse3_fetch_horizontal (
Packit 030a23
	    &iter->image->bits, line1, y1, fx, ux, iter->width);
Packit 030a23
    }
Packit 030a23
Packit 030a23
    dist_y = pixman_fixed_to_bilinear_weight (info->y);
Packit 030a23
    dist_y <<= (16 - BILINEAR_INTERPOLATION_BITS);
Packit 030a23
Packit 030a23
    vw = _mm_set_epi16 (
Packit 030a23
	dist_y, dist_y, dist_y, dist_y, dist_y, dist_y, dist_y, dist_y);
Packit 030a23
Packit 030a23
    for (i = 0; i + 3 < iter->width; i += 4)
Packit 030a23
    {
Packit 030a23
	__m128i top0 = _mm_load_si128 ((__m128i *)(line0->buffer + i));
Packit 030a23
	__m128i bot0 = _mm_load_si128 ((__m128i *)(line1->buffer + i));
Packit 030a23
	__m128i top1 = _mm_load_si128 ((__m128i *)(line0->buffer + i + 2));
Packit 030a23
	__m128i bot1 = _mm_load_si128 ((__m128i *)(line1->buffer + i + 2));
Packit 030a23
	__m128i r0, r1, tmp, p;
Packit 030a23
Packit 030a23
	r0 = _mm_mulhi_epu16 (
Packit 030a23
	    _mm_sub_epi16 (bot0, top0), vw);
Packit 030a23
	tmp = _mm_cmplt_epi16 (bot0, top0);
Packit 030a23
	tmp = _mm_and_si128 (tmp, vw);
Packit 030a23
	r0 = _mm_sub_epi16 (r0, tmp);
Packit 030a23
	r0 = _mm_add_epi16 (r0, top0);
Packit 030a23
	r0 = _mm_srli_epi16 (r0, BILINEAR_INTERPOLATION_BITS);
Packit 030a23
	/* r0:  A0 R0 A1 R1 G0 B0 G1 B1 */
Packit 030a23
	r0 = _mm_shuffle_epi32 (r0, _MM_SHUFFLE (2, 0, 3, 1));
Packit 030a23
	/* r0:  A1 R1 G1 B1 A0 R0 G0 B0 */
Packit 030a23
Packit 030a23
	r1 = _mm_mulhi_epu16 (
Packit 030a23
	    _mm_sub_epi16 (bot1, top1), vw);
Packit 030a23
	tmp = _mm_cmplt_epi16 (bot1, top1);
Packit 030a23
	tmp = _mm_and_si128 (tmp, vw);
Packit 030a23
	r1 = _mm_sub_epi16 (r1, tmp);
Packit 030a23
	r1 = _mm_add_epi16 (r1, top1);
Packit 030a23
	r1 = _mm_srli_epi16 (r1, BILINEAR_INTERPOLATION_BITS);
Packit 030a23
	r1 = _mm_shuffle_epi32 (r1, _MM_SHUFFLE (2, 0, 3, 1));
Packit 030a23
	/* r1: A3 R3 G3 B3 A2 R2 G2 B2 */
Packit 030a23
Packit 030a23
	p = _mm_packus_epi16 (r0, r1);
Packit 030a23
Packit 030a23
	_mm_storeu_si128 ((__m128i *)(iter->buffer + i), p);
Packit 030a23
    }
Packit 030a23
Packit 030a23
    while (i < iter->width)
Packit 030a23
    {
Packit 030a23
	__m128i top0 = _mm_load_si128 ((__m128i *)(line0->buffer + i));
Packit 030a23
	__m128i bot0 = _mm_load_si128 ((__m128i *)(line1->buffer + i));
Packit 030a23
	__m128i r0, tmp, p;
Packit 030a23
Packit 030a23
	r0 = _mm_mulhi_epu16 (
Packit 030a23
	    _mm_sub_epi16 (bot0, top0), vw);
Packit 030a23
	tmp = _mm_cmplt_epi16 (bot0, top0);
Packit 030a23
	tmp = _mm_and_si128 (tmp, vw);
Packit 030a23
	r0 = _mm_sub_epi16 (r0, tmp);
Packit 030a23
	r0 = _mm_add_epi16 (r0, top0);
Packit 030a23
	r0 = _mm_srli_epi16 (r0, BILINEAR_INTERPOLATION_BITS);
Packit 030a23
	/* r0:  A0 R0 A1 R1 G0 B0 G1 B1 */
Packit 030a23
	r0 = _mm_shuffle_epi32 (r0, _MM_SHUFFLE (2, 0, 3, 1));
Packit 030a23
	/* r0:  A1 R1 G1 B1 A0 R0 G0 B0 */
Packit 030a23
Packit 030a23
	p = _mm_packus_epi16 (r0, r0);
Packit 030a23
Packit 030a23
	if (iter->width - i == 1)
Packit 030a23
	{
Packit 030a23
	    *(uint32_t *)(iter->buffer + i) = _mm_cvtsi128_si32 (p);
Packit 030a23
	    i++;
Packit 030a23
	}
Packit 030a23
	else
Packit 030a23
	{
Packit 030a23
	    _mm_storel_epi64 ((__m128i *)(iter->buffer + i), p);
Packit 030a23
	    i += 2;
Packit 030a23
	}
Packit 030a23
    }
Packit 030a23
    
Packit 030a23
    info->y += iter->image->common.transform->matrix[1][1];
Packit 030a23
Packit 030a23
    return iter->buffer;
Packit 030a23
}
Packit 030a23
Packit 030a23
static void
Packit 030a23
ssse3_bilinear_cover_iter_fini (pixman_iter_t *iter)
Packit 030a23
{
Packit 030a23
    free (iter->data);
Packit 030a23
}
Packit 030a23
Packit 030a23
static void
Packit 030a23
ssse3_bilinear_cover_iter_init (pixman_iter_t *iter, const pixman_iter_info_t *iter_info)
Packit 030a23
{
Packit 030a23
    int width = iter->width;
Packit 030a23
    bilinear_info_t *info;
Packit 030a23
    pixman_vector_t v;
Packit 030a23
Packit 030a23
    /* Reference point is the center of the pixel */
Packit 030a23
    v.vector[0] = pixman_int_to_fixed (iter->x) + pixman_fixed_1 / 2;
Packit 030a23
    v.vector[1] = pixman_int_to_fixed (iter->y) + pixman_fixed_1 / 2;
Packit 030a23
    v.vector[2] = pixman_fixed_1;
Packit 030a23
Packit 030a23
    if (!pixman_transform_point_3d (iter->image->common.transform, &v))
Packit 030a23
	goto fail;
Packit 030a23
Packit 030a23
    info = malloc (sizeof (*info) + (2 * width - 1) * sizeof (uint64_t) + 64);
Packit 030a23
    if (!info)
Packit 030a23
	goto fail;
Packit 030a23
Packit 030a23
    info->x = v.vector[0] - pixman_fixed_1 / 2;
Packit 030a23
    info->y = v.vector[1] - pixman_fixed_1 / 2;
Packit 030a23
Packit 030a23
#define ALIGN(addr)							\
Packit 030a23
    ((void *)((((uintptr_t)(addr)) + 15) & (~15)))
Packit 030a23
Packit 030a23
    /* It is safe to set the y coordinates to -1 initially
Packit 030a23
     * because COVER_CLIP_BILINEAR ensures that we will only
Packit 030a23
     * be asked to fetch lines in the [0, height) interval
Packit 030a23
     */
Packit 030a23
    info->lines[0].y = -1;
Packit 030a23
    info->lines[0].buffer = ALIGN (&(info->data[0]));
Packit 030a23
    info->lines[1].y = -1;
Packit 030a23
    info->lines[1].buffer = ALIGN (info->lines[0].buffer + width);
Packit 030a23
Packit 030a23
    iter->get_scanline = ssse3_fetch_bilinear_cover;
Packit 030a23
    iter->fini = ssse3_bilinear_cover_iter_fini;
Packit 030a23
Packit 030a23
    iter->data = info;
Packit 030a23
    return;
Packit 030a23
Packit 030a23
fail:
Packit 030a23
    /* Something went wrong, either a bad matrix or OOM; in such cases,
Packit 030a23
     * we don't guarantee any particular rendering.
Packit 030a23
     */
Packit 030a23
    _pixman_log_error (
Packit 030a23
	FUNC, "Allocation failure or bad matrix, skipping rendering\n");
Packit 030a23
    
Packit 030a23
    iter->get_scanline = _pixman_iter_get_scanline_noop;
Packit 030a23
    iter->fini = NULL;
Packit 030a23
}
Packit 030a23
Packit 030a23
static const pixman_iter_info_t ssse3_iters[] = 
Packit 030a23
{
Packit 030a23
    { PIXMAN_a8r8g8b8,
Packit 030a23
      (FAST_PATH_STANDARD_FLAGS			|
Packit 030a23
       FAST_PATH_SCALE_TRANSFORM		|
Packit 030a23
       FAST_PATH_BILINEAR_FILTER		|
Packit 030a23
       FAST_PATH_SAMPLES_COVER_CLIP_BILINEAR),
Packit 030a23
      ITER_NARROW | ITER_SRC,
Packit 030a23
      ssse3_bilinear_cover_iter_init,
Packit 030a23
      NULL, NULL
Packit 030a23
    },
Packit 030a23
Packit 030a23
    { PIXMAN_null },
Packit 030a23
};
Packit 030a23
Packit 030a23
static const pixman_fast_path_t ssse3_fast_paths[] =
Packit 030a23
{
Packit 030a23
    { PIXMAN_OP_NONE },
Packit 030a23
};
Packit 030a23
Packit 030a23
pixman_implementation_t *
Packit 030a23
_pixman_implementation_create_ssse3 (pixman_implementation_t *fallback)
Packit 030a23
{
Packit 030a23
    pixman_implementation_t *imp =
Packit 030a23
	_pixman_implementation_create (fallback, ssse3_fast_paths);
Packit 030a23
Packit 030a23
    imp->iter_info = ssse3_iters;
Packit 030a23
Packit 030a23
    return imp;
Packit 030a23
}