blob: 9bf65d8045e89d7514ab389673d8eab7455d86f3 [file] [log] [blame]
/*
* Copyright (c) 2018 The WebM project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include <assert.h>
#include <xmmintrin.h>
#include "./vp8_rtcd.h"
#include "./vpx_config.h"
#include "vp8/common/filter.h"
#include "vpx_dsp/x86/mem_sse2.h"
#include "vpx_ports/mem.h"
static INLINE void horizontal_16x16(uint8_t *src, const int stride,
uint16_t *dst, const int xoffset) {
int h;
const __m128i zero = _mm_setzero_si128();
if (xoffset == 0) {
for (h = 0; h < 17; ++h) {
const __m128i a = _mm_loadu_si128((__m128i *)src);
const __m128i a_lo = _mm_unpacklo_epi8(a, zero);
const __m128i a_hi = _mm_unpackhi_epi8(a, zero);
_mm_store_si128((__m128i *)dst, a_lo);
_mm_store_si128((__m128i *)(dst + 8), a_hi);
src += stride;
dst += 16;
}
return;
}
{
const __m128i round_factor = _mm_set1_epi16(1 << (VP8_FILTER_SHIFT - 1));
const __m128i hfilter_0 = _mm_set1_epi16(vp8_bilinear_filters[xoffset][0]);
const __m128i hfilter_1 = _mm_set1_epi16(vp8_bilinear_filters[xoffset][1]);
for (h = 0; h < 17; ++h) {
const __m128i a = _mm_loadu_si128((__m128i *)src);
const __m128i a_lo = _mm_unpacklo_epi8(a, zero);
const __m128i a_hi = _mm_unpackhi_epi8(a, zero);
const __m128i a_lo_filtered = _mm_mullo_epi16(a_lo, hfilter_0);
const __m128i a_hi_filtered = _mm_mullo_epi16(a_hi, hfilter_0);
const __m128i b = _mm_loadu_si128((__m128i *)(src + 1));
const __m128i b_lo = _mm_unpacklo_epi8(b, zero);
const __m128i b_hi = _mm_unpackhi_epi8(b, zero);
const __m128i b_lo_filtered = _mm_mullo_epi16(b_lo, hfilter_1);
const __m128i b_hi_filtered = _mm_mullo_epi16(b_hi, hfilter_1);
const __m128i sum_lo = _mm_add_epi16(a_lo_filtered, b_lo_filtered);
const __m128i sum_hi = _mm_add_epi16(a_hi_filtered, b_hi_filtered);
const __m128i compensated_lo = _mm_add_epi16(sum_lo, round_factor);
const __m128i compensated_hi = _mm_add_epi16(sum_hi, round_factor);
const __m128i shifted_lo =
_mm_srai_epi16(compensated_lo, VP8_FILTER_SHIFT);
const __m128i shifted_hi =
_mm_srai_epi16(compensated_hi, VP8_FILTER_SHIFT);
_mm_store_si128((__m128i *)dst, shifted_lo);
_mm_store_si128((__m128i *)(dst + 8), shifted_hi);
src += stride;
dst += 16;
}
}
}
static INLINE void vertical_16x16(uint16_t *src, uint8_t *dst, const int stride,
const int yoffset) {
int h;
if (yoffset == 0) {
for (h = 0; h < 16; ++h) {
const __m128i row_lo = _mm_load_si128((__m128i *)src);
const __m128i row_hi = _mm_load_si128((__m128i *)(src + 8));
const __m128i packed = _mm_packus_epi16(row_lo, row_hi);
_mm_store_si128((__m128i *)dst, packed);
src += 16;
dst += stride;
}
return;
}
{
const __m128i round_factor = _mm_set1_epi16(1 << (VP8_FILTER_SHIFT - 1));
const __m128i vfilter_0 = _mm_set1_epi16(vp8_bilinear_filters[yoffset][0]);
const __m128i vfilter_1 = _mm_set1_epi16(vp8_bilinear_filters[yoffset][1]);
__m128i row_0_lo = _mm_load_si128((__m128i *)src);
__m128i row_0_hi = _mm_load_si128((__m128i *)(src + 8));
src += 16;
for (h = 0; h < 16; ++h) {
const __m128i row_0_lo_filtered = _mm_mullo_epi16(row_0_lo, vfilter_0);
const __m128i row_0_hi_filtered = _mm_mullo_epi16(row_0_hi, vfilter_0);
const __m128i row_1_lo = _mm_load_si128((__m128i *)src);
const __m128i row_1_hi = _mm_load_si128((__m128i *)(src + 8));
const __m128i row_1_lo_filtered = _mm_mullo_epi16(row_1_lo, vfilter_1);
const __m128i row_1_hi_filtered = _mm_mullo_epi16(row_1_hi, vfilter_1);
const __m128i sum_lo =
_mm_add_epi16(row_0_lo_filtered, row_1_lo_filtered);
const __m128i sum_hi =
_mm_add_epi16(row_0_hi_filtered, row_1_hi_filtered);
const __m128i compensated_lo = _mm_add_epi16(sum_lo, round_factor);
const __m128i compensated_hi = _mm_add_epi16(sum_hi, round_factor);
const __m128i shifted_lo =
_mm_srai_epi16(compensated_lo, VP8_FILTER_SHIFT);
const __m128i shifted_hi =
_mm_srai_epi16(compensated_hi, VP8_FILTER_SHIFT);
const __m128i packed = _mm_packus_epi16(shifted_lo, shifted_hi);
_mm_store_si128((__m128i *)dst, packed);
row_0_lo = row_1_lo;
row_0_hi = row_1_hi;
src += 16;
dst += stride;
}
}
}
void vp8_bilinear_predict16x16_sse2(uint8_t *src_ptr, int src_pixels_per_line,
int xoffset, int yoffset, uint8_t *dst_ptr,
int dst_pitch) {
DECLARE_ALIGNED(16, uint16_t, FData[16 * 17]);
assert((xoffset | yoffset) != 0);
horizontal_16x16(src_ptr, src_pixels_per_line, FData, xoffset);
vertical_16x16(FData, dst_ptr, dst_pitch, yoffset);
}
static INLINE void horizontal_8xN(uint8_t *src, const int stride, uint16_t *dst,
const int xoffset, const int height) {
int h;
const __m128i zero = _mm_setzero_si128();
if (xoffset == 0) {
for (h = 0; h < height; ++h) {
const __m128i a = _mm_loadl_epi64((__m128i *)src);
const __m128i a_u16 = _mm_unpacklo_epi8(a, zero);
_mm_store_si128((__m128i *)dst, a_u16);
src += stride;
dst += 8;
}
return;
}
{
const __m128i round_factor = _mm_set1_epi16(1 << (VP8_FILTER_SHIFT - 1));
const __m128i hfilter_0 = _mm_set1_epi16(vp8_bilinear_filters[xoffset][0]);
const __m128i hfilter_1 = _mm_set1_epi16(vp8_bilinear_filters[xoffset][1]);
// Filter horizontally. Rather than load the whole array and transpose, load
// 16 values (overreading) and shift to set up the second value. Do an
// "extra" 9th line so the vertical pass has the necessary context.
for (h = 0; h < height; ++h) {
const __m128i a = _mm_loadu_si128((__m128i *)src);
const __m128i b = _mm_srli_si128(a, 1);
const __m128i a_u16 = _mm_unpacklo_epi8(a, zero);
const __m128i b_u16 = _mm_unpacklo_epi8(b, zero);
const __m128i a_filtered = _mm_mullo_epi16(a_u16, hfilter_0);
const __m128i b_filtered = _mm_mullo_epi16(b_u16, hfilter_1);
const __m128i sum = _mm_add_epi16(a_filtered, b_filtered);
const __m128i compensated = _mm_add_epi16(sum, round_factor);
const __m128i shifted = _mm_srai_epi16(compensated, VP8_FILTER_SHIFT);
_mm_store_si128((__m128i *)dst, shifted);
src += stride;
dst += 8;
}
}
}
static INLINE void vertical_8xN(uint16_t *src, uint8_t *dst, const int stride,
const int yoffset, const int height) {
int h;
if (yoffset == 0) {
for (h = 0; h < height; ++h) {
const __m128i row = _mm_load_si128((__m128i *)src);
const __m128i packed = _mm_packus_epi16(row, row);
_mm_storel_epi64((__m128i *)dst, packed);
src += 8;
dst += stride;
}
return;
}
{
const __m128i round_factor = _mm_set1_epi16(1 << (VP8_FILTER_SHIFT - 1));
const __m128i vfilter_0 = _mm_set1_epi16(vp8_bilinear_filters[yoffset][0]);
const __m128i vfilter_1 = _mm_set1_epi16(vp8_bilinear_filters[yoffset][1]);
__m128i row_0 = _mm_load_si128((__m128i *)src);
src += 8;
for (h = 0; h < height; ++h) {
const __m128i row_1 = _mm_load_si128((__m128i *)src);
const __m128i row_0_filtered = _mm_mullo_epi16(row_0, vfilter_0);
const __m128i row_1_filtered = _mm_mullo_epi16(row_1, vfilter_1);
const __m128i sum = _mm_add_epi16(row_0_filtered, row_1_filtered);
const __m128i compensated = _mm_add_epi16(sum, round_factor);
const __m128i shifted = _mm_srai_epi16(compensated, VP8_FILTER_SHIFT);
const __m128i packed = _mm_packus_epi16(shifted, shifted);
_mm_storel_epi64((__m128i *)dst, packed);
row_0 = row_1;
src += 8;
dst += stride;
}
}
}
void vp8_bilinear_predict8x8_sse2(uint8_t *src_ptr, int src_pixels_per_line,
int xoffset, int yoffset, uint8_t *dst_ptr,
int dst_pitch) {
DECLARE_ALIGNED(16, uint16_t, FData[8 * 9]);
assert((xoffset | yoffset) != 0);
horizontal_8xN(src_ptr, src_pixels_per_line, FData, xoffset, 9);
vertical_8xN(FData, dst_ptr, dst_pitch, yoffset, 8);
}
void vp8_bilinear_predict8x4_sse2(uint8_t *src_ptr, int src_pixels_per_line,
int xoffset, int yoffset, uint8_t *dst_ptr,
int dst_pitch) {
DECLARE_ALIGNED(16, uint16_t, FData[8 * 5]);
assert((xoffset | yoffset) != 0);
horizontal_8xN(src_ptr, src_pixels_per_line, FData, xoffset, 5);
vertical_8xN(FData, dst_ptr, dst_pitch, yoffset, 4);
}
static INLINE void horizontal_4x4(uint8_t *src, const int stride, uint16_t *dst,
const int xoffset) {
int h;
const __m128i zero = _mm_setzero_si128();
if (xoffset == 0) {
for (h = 0; h < 5; ++h) {
const __m128i a = load_unaligned_u32(src);
const __m128i a_u16 = _mm_unpacklo_epi8(a, zero);
_mm_storel_epi64((__m128i *)dst, a_u16);
src += stride;
dst += 4;
}
return;
}
{
const __m128i round_factor = _mm_set1_epi16(1 << (VP8_FILTER_SHIFT - 1));
const __m128i hfilter_0 = _mm_set1_epi16(vp8_bilinear_filters[xoffset][0]);
const __m128i hfilter_1 = _mm_set1_epi16(vp8_bilinear_filters[xoffset][1]);
for (h = 0; h < 5; ++h) {
const __m128i a = load_unaligned_u32(src);
const __m128i b = load_unaligned_u32(src + 1);
const __m128i a_u16 = _mm_unpacklo_epi8(a, zero);
const __m128i b_u16 = _mm_unpacklo_epi8(b, zero);
const __m128i a_filtered = _mm_mullo_epi16(a_u16, hfilter_0);
const __m128i b_filtered = _mm_mullo_epi16(b_u16, hfilter_1);
const __m128i sum = _mm_add_epi16(a_filtered, b_filtered);
const __m128i compensated = _mm_add_epi16(sum, round_factor);
const __m128i shifted = _mm_srai_epi16(compensated, VP8_FILTER_SHIFT);
_mm_storel_epi64((__m128i *)dst, shifted);
src += stride;
dst += 4;
}
}
}
static INLINE void vertical_4x4(uint16_t *src, uint8_t *dst, const int stride,
const int yoffset) {
int h;
if (yoffset == 0) {
for (h = 0; h < 4; h += 2) {
const __m128i row = _mm_load_si128((__m128i *)src);
__m128i packed = _mm_packus_epi16(row, row);
store_unaligned_u32(dst, packed);
dst += stride;
packed = _mm_srli_si128(packed, 4);
store_unaligned_u32(dst, packed);
dst += stride;
src += 8;
}
return;
}
{
const __m128i round_factor = _mm_set1_epi16(1 << (VP8_FILTER_SHIFT - 1));
const __m128i vfilter_0 = _mm_set1_epi16(vp8_bilinear_filters[yoffset][0]);
const __m128i vfilter_1 = _mm_set1_epi16(vp8_bilinear_filters[yoffset][1]);
for (h = 0; h < 4; h += 2) {
const __m128i row_0 = _mm_load_si128((__m128i *)src);
const __m128i row_1 = _mm_loadu_si128((__m128i *)(src + 4));
const __m128i row_0_filtered = _mm_mullo_epi16(row_0, vfilter_0);
const __m128i row_1_filtered = _mm_mullo_epi16(row_1, vfilter_1);
const __m128i sum = _mm_add_epi16(row_0_filtered, row_1_filtered);
const __m128i compensated = _mm_add_epi16(sum, round_factor);
const __m128i shifted = _mm_srai_epi16(compensated, VP8_FILTER_SHIFT);
__m128i packed = _mm_packus_epi16(shifted, shifted);
storeu_uint32(dst, _mm_cvtsi128_si32(packed));
packed = _mm_srli_si128(packed, 4);
dst += stride;
storeu_uint32(dst, _mm_cvtsi128_si32(packed));
dst += stride;
src += 8;
}
}
}
void vp8_bilinear_predict4x4_sse2(uint8_t *src_ptr, int src_pixels_per_line,
int xoffset, int yoffset, uint8_t *dst_ptr,
int dst_pitch) {
DECLARE_ALIGNED(16, uint16_t, FData[4 * 5]);
assert((xoffset | yoffset) != 0);
horizontal_4x4(src_ptr, src_pixels_per_line, FData, xoffset);
vertical_4x4(FData, dst_ptr, dst_pitch, yoffset);
}