Optimize SSE2 warp filter
Improve the speed of the warp filter itself by ~30%. This leads
to an overall decoder speedup of 5-20%, depending on bitrate,
for the global-motion experiment, and a small speedup for
warped-motion.
Applies a very minor change to the rounding during filter
selection (ROUND_POWER_OF_TWO makes slightly more sense here
than ROUND_POWER_OF_TWO_SIGNED, and is faster)
Change-Id: I3f364221d1ec35a8aac0d2c8b0e427f527d12e43
diff --git a/av1/common/warped_motion.c b/av1/common/warped_motion.c
index d1107d5..d6c5279 100644
--- a/av1/common/warped_motion.c
+++ b/av1/common/warped_motion.c
@@ -992,8 +992,7 @@
int ix = ix4 + l - 3;
// At this point, sx = sx4 + alpha * l + beta * k
const int16_t *coeffs =
- warped_filter[ROUND_POWER_OF_TWO_SIGNED(sx,
- WARPEDDIFF_PREC_BITS) +
+ warped_filter[ROUND_POWER_OF_TWO(sx, WARPEDDIFF_PREC_BITS) +
WARPEDPIXEL_PREC_SHIFTS];
int32_t sum = 0;
for (m = 0; m < 8; ++m) {
@@ -1012,9 +1011,9 @@
uint8_t *p =
&pred[(i - p_row + k + 4) * p_stride + (j - p_col + l + 4)];
// At this point, sy = sy4 + gamma * l + delta * k
- const int16_t *coeffs = warped_filter[ROUND_POWER_OF_TWO_SIGNED(
- sy, WARPEDDIFF_PREC_BITS) +
- WARPEDPIXEL_PREC_SHIFTS];
+ const int16_t *coeffs =
+ warped_filter[ROUND_POWER_OF_TWO(sy, WARPEDDIFF_PREC_BITS) +
+ WARPEDPIXEL_PREC_SHIFTS];
int32_t sum = 0;
for (m = 0; m < 8; ++m) {
sum += tmp[(k + m + 4) * 8 + (l + 4)] * coeffs[m];
diff --git a/av1/common/x86/warp_plane_sse2.c b/av1/common/x86/warp_plane_sse2.c
index 5ecc30e..395797a 100644
--- a/av1/common/x86/warp_plane_sse2.c
+++ b/av1/common/x86/warp_plane_sse2.c
@@ -14,6 +14,8 @@
#include "./av1_rtcd.h"
#include "av1/common/warped_motion.h"
+const __m128i *const filter = (const __m128i *const)warped_filter;
+
/* SSE2 version of the rotzoom/affine warp filter */
void warp_affine_sse2(int32_t *mat, uint8_t *ref, int width, int height,
int stride, uint8_t *pred, int p_col, int p_row,
@@ -22,7 +24,7 @@
int32_t alpha, int32_t beta, int32_t gamma,
int32_t delta) {
__m128i tmp[15];
- int i, j, k, l;
+ int i, j, k;
/* Note: For this code to work, the left/right frame borders need to be
extended by at least 13 pixels each. By the time we get here, other
@@ -36,24 +38,29 @@
}
}*/
- for (i = p_row; i < p_row + p_height; i += 8) {
- for (j = p_col; j < p_col + p_width; j += 8) {
+ for (i = 0; i < p_height; i += 8) {
+ for (j = 0; j < p_width; j += 8) {
+ // (x, y) coordinates of the center of this block in the destination
+ // image
+ int32_t dst_x = p_col + j + 4;
+ int32_t dst_y = p_row + i + 4;
+
int32_t x4, y4, ix4, sx4, iy4, sy4;
if (subsampling_x)
x4 = ROUND_POWER_OF_TWO_SIGNED(
- mat[2] * 2 * (j + 4) + mat[3] * 2 * (i + 4) + mat[0] +
+ mat[2] * 2 * dst_x + mat[3] * 2 * dst_y + mat[0] +
(mat[2] + mat[3] - (1 << WARPEDMODEL_PREC_BITS)) / 2,
1);
else
- x4 = mat[2] * (j + 4) + mat[3] * (i + 4) + mat[0];
+ x4 = mat[2] * dst_x + mat[3] * dst_y + mat[0];
if (subsampling_y)
y4 = ROUND_POWER_OF_TWO_SIGNED(
- mat[4] * 2 * (j + 4) + mat[5] * 2 * (i + 4) + mat[1] +
+ mat[4] * 2 * dst_x + mat[5] * 2 * dst_y + mat[1] +
(mat[4] + mat[5] - (1 << WARPEDMODEL_PREC_BITS)) / 2,
1);
else
- y4 = mat[4] * (j + 4) + mat[5] * (i + 4) + mat[1];
+ y4 = mat[4] * dst_x + mat[5] * dst_y + mat[1];
ix4 = x4 >> WARPEDMODEL_PREC_BITS;
sx4 = x4 & ((1 << WARPEDMODEL_PREC_BITS) - 1);
@@ -61,214 +68,196 @@
sy4 = y4 & ((1 << WARPEDMODEL_PREC_BITS) - 1);
// Horizontal filter
- for (k = -7; k < 8; ++k) {
+ for (k = -7; k < AOMMIN(8, p_height - i); ++k) {
int iy = iy4 + k;
if (iy < 0)
iy = 0;
else if (iy > height - 1)
iy = height - 1;
+ // If the block is aligned such that, after clamping, every sample
+ // would be taken from the leftmost/rightmost column, then we can
+ // skip the expensive horizontal filter.
if (ix4 <= -7) {
- // In this case, the rightmost pixel sampled is in column
- // ix4 + 3 + 7 - 3 = ix4 + 7 <= 0, ie. the entire block
- // will sample only from the leftmost column
- // (once border extension is taken into account)
- for (l = 0; l < 8; ++l) {
- ((int16_t *)tmp)[(k + 7) * 8 + l] =
- ref[iy * stride] * (1 << WARPEDPIXEL_FILTER_BITS);
- }
+ tmp[k + 7] =
+ _mm_set1_epi16(ref[iy * stride] * (1 << WARPEDPIXEL_FILTER_BITS));
} else if (ix4 >= width + 6) {
- // In this case, the leftmost pixel sampled is in column
- // ix4 - 3 + 0 - 3 = ix4 - 6 >= width, ie. the entire block
- // will sample only from the rightmost column
- // (once border extension is taken into account)
- for (l = 0; l < 8; ++l) {
- ((int16_t *)tmp)[(k + 7) * 8 + l] =
- ref[iy * stride + (width - 1)] * (1 << WARPEDPIXEL_FILTER_BITS);
- }
+ tmp[k + 7] = _mm_set1_epi16(ref[iy * stride + (width - 1)] *
+ (1 << WARPEDPIXEL_FILTER_BITS));
} else {
- // If we get here, then
- // the leftmost pixel sampled is
- // ix4 - 4 + 0 - 3 = ix4 - 7 >= -13
- // and the rightmost pixel sampled is at most
- // ix4 + 3 + 7 - 3 = ix4 + 7 <= width + 12
- // So, assuming that border extension has been done, we
- // don't need to explicitly clamp values.
- int sx = sx4 + alpha * (-4) + beta * k;
+ int sx = sx4 + alpha * (-4) + beta * k +
+ // Include rounding and offset here
+ (1 << (WARPEDDIFF_PREC_BITS - 1)) +
+ (WARPEDPIXEL_PREC_SHIFTS << WARPEDDIFF_PREC_BITS);
- // Load per-pixel coefficients
- __m128i coeffs[8];
- for (l = 0; l < 8; ++l) {
- coeffs[l] =
- ((__m128i *)warped_filter)[ROUND_POWER_OF_TWO_SIGNED(
- sx, WARPEDDIFF_PREC_BITS) +
- WARPEDPIXEL_PREC_SHIFTS];
- sx += alpha;
- }
+ // Load source pixels
+ __m128i zero = _mm_setzero_si128();
+ __m128i src =
+ _mm_loadu_si128((__m128i *)(ref + iy * stride + ix4 - 7));
- // Currently the coefficients are stored in the order
- // 0 ... 7 for each pixel separately.
- // Permute the coefficients into 8 registers:
- // coeffs (0,1), (2,3), (4,5), (6,7) for
- // i) the odd-index pixels, and ii) the even-index pixels
- {
- // 0 1 0 1 2 3 2 3 for 0, 2
- __m128i tmp_0 = _mm_unpacklo_epi32(coeffs[0], coeffs[2]);
- // 0 1 0 1 2 3 2 3 for 1, 3
- __m128i tmp_1 = _mm_unpacklo_epi32(coeffs[1], coeffs[3]);
- // 0 1 0 1 2 3 2 3 for 4, 6
- __m128i tmp_2 = _mm_unpacklo_epi32(coeffs[4], coeffs[6]);
- // 0 1 0 1 2 3 2 3 for 5, 7
- __m128i tmp_3 = _mm_unpacklo_epi32(coeffs[5], coeffs[7]);
- // 4 5 4 5 6 7 6 7 for 0, 2
- __m128i tmp_4 = _mm_unpackhi_epi32(coeffs[0], coeffs[2]);
- // 4 5 4 5 6 7 6 7 for 1, 3
- __m128i tmp_5 = _mm_unpackhi_epi32(coeffs[1], coeffs[3]);
- // 4 5 4 5 6 7 6 7 for 4, 6
- __m128i tmp_6 = _mm_unpackhi_epi32(coeffs[4], coeffs[6]);
- // 4 5 4 5 6 7 6 7 for 5, 7
- __m128i tmp_7 = _mm_unpackhi_epi32(coeffs[5], coeffs[7]);
+ // Filter even-index pixels
+ __m128i tmp_0 = filter[(sx + 0 * alpha) >> WARPEDDIFF_PREC_BITS];
+ __m128i tmp_2 = filter[(sx + 2 * alpha) >> WARPEDDIFF_PREC_BITS];
+ __m128i tmp_4 = filter[(sx + 4 * alpha) >> WARPEDDIFF_PREC_BITS];
+ __m128i tmp_6 = filter[(sx + 6 * alpha) >> WARPEDDIFF_PREC_BITS];
- // 0 1 0 1 0 1 0 1 for 0, 2, 4, 6
- __m128i coeff_a = _mm_unpacklo_epi64(tmp_0, tmp_2);
- // 0 1 0 1 0 1 0 1 for 1, 3, 5, 7
- __m128i coeff_b = _mm_unpacklo_epi64(tmp_1, tmp_3);
- // 2 3 2 3 2 3 2 3 for 0, 2, 4, 6
- __m128i coeff_c = _mm_unpackhi_epi64(tmp_0, tmp_2);
- // 2 3 2 3 2 3 2 3 for 1, 3, 5, 7
- __m128i coeff_d = _mm_unpackhi_epi64(tmp_1, tmp_3);
- // 4 5 4 5 4 5 4 5 for 0, 2, 4, 6
- __m128i coeff_e = _mm_unpacklo_epi64(tmp_4, tmp_6);
- // 4 5 4 5 4 5 4 5 for 1, 3, 5, 7
- __m128i coeff_f = _mm_unpacklo_epi64(tmp_5, tmp_7);
- // 6 7 6 7 6 7 6 7 for 0, 2, 4, 6
- __m128i coeff_g = _mm_unpackhi_epi64(tmp_4, tmp_6);
- // 6 7 6 7 6 7 6 7 for 1, 3, 5, 7
- __m128i coeff_h = _mm_unpackhi_epi64(tmp_5, tmp_7);
+ // coeffs 0 1 0 1 2 3 2 3 for pixels 0, 2
+ __m128i tmp_8 = _mm_unpacklo_epi32(tmp_0, tmp_2);
+ // coeffs 0 1 0 1 2 3 2 3 for pixels 4, 6
+ __m128i tmp_10 = _mm_unpacklo_epi32(tmp_4, tmp_6);
+ // coeffs 4 5 4 5 6 7 6 7 for pixels 0, 2
+ __m128i tmp_12 = _mm_unpackhi_epi32(tmp_0, tmp_2);
+ // coeffs 4 5 4 5 6 7 6 7 for pixels 4, 6
+ __m128i tmp_14 = _mm_unpackhi_epi32(tmp_4, tmp_6);
- // Load source pixels and widen to 16 bits. We want the pixels
- // ref[iy * stride + ix4 - 7] ... ref[iy * stride + ix4 + 7]
- // inclusive.
- __m128i zero = _mm_setzero_si128();
- __m128i src =
- _mm_loadu_si128((__m128i *)(ref + iy * stride + ix4 - 7));
+ // coeffs 0 1 0 1 0 1 0 1 for pixels 0, 2, 4, 6
+ __m128i coeff_0 = _mm_unpacklo_epi64(tmp_8, tmp_10);
+ // coeffs 2 3 2 3 2 3 2 3 for pixels 0, 2, 4, 6
+ __m128i coeff_2 = _mm_unpackhi_epi64(tmp_8, tmp_10);
+ // coeffs 4 5 4 5 4 5 4 5 for pixels 0, 2, 4, 6
+ __m128i coeff_4 = _mm_unpacklo_epi64(tmp_12, tmp_14);
+ // coeffs 6 7 6 7 6 7 6 7 for pixels 0, 2, 4, 6
+ __m128i coeff_6 = _mm_unpackhi_epi64(tmp_12, tmp_14);
- // Calculate filtered results
- __m128i src_a = _mm_unpacklo_epi8(src, zero);
- __m128i res_a = _mm_madd_epi16(src_a, coeff_a);
- __m128i src_b = _mm_unpacklo_epi8(_mm_srli_si128(src, 1), zero);
- __m128i res_b = _mm_madd_epi16(src_b, coeff_b);
- __m128i src_c = _mm_unpacklo_epi8(_mm_srli_si128(src, 2), zero);
- __m128i res_c = _mm_madd_epi16(src_c, coeff_c);
- __m128i src_d = _mm_unpacklo_epi8(_mm_srli_si128(src, 3), zero);
- __m128i res_d = _mm_madd_epi16(src_d, coeff_d);
- __m128i src_e = _mm_unpacklo_epi8(_mm_srli_si128(src, 4), zero);
- __m128i res_e = _mm_madd_epi16(src_e, coeff_e);
- __m128i src_f = _mm_unpacklo_epi8(_mm_srli_si128(src, 5), zero);
- __m128i res_f = _mm_madd_epi16(src_f, coeff_f);
- __m128i src_g = _mm_unpacklo_epi8(_mm_srli_si128(src, 6), zero);
- __m128i res_g = _mm_madd_epi16(src_g, coeff_g);
- __m128i src_h = _mm_unpacklo_epi8(_mm_srli_si128(src, 7), zero);
- __m128i res_h = _mm_madd_epi16(src_h, coeff_h);
+ // Calculate filtered results
+ __m128i src_0 = _mm_unpacklo_epi8(src, zero);
+ __m128i res_0 = _mm_madd_epi16(src_0, coeff_0);
+ __m128i src_2 = _mm_unpacklo_epi8(_mm_srli_si128(src, 2), zero);
+ __m128i res_2 = _mm_madd_epi16(src_2, coeff_2);
+ __m128i src_4 = _mm_unpacklo_epi8(_mm_srli_si128(src, 4), zero);
+ __m128i res_4 = _mm_madd_epi16(src_4, coeff_4);
+ __m128i src_6 = _mm_unpacklo_epi8(_mm_srli_si128(src, 6), zero);
+ __m128i res_6 = _mm_madd_epi16(src_6, coeff_6);
- __m128i res_even = _mm_add_epi32(_mm_add_epi32(res_a, res_e),
- _mm_add_epi32(res_c, res_g));
- __m128i res_odd = _mm_add_epi32(_mm_add_epi32(res_b, res_f),
- _mm_add_epi32(res_d, res_h));
+ __m128i res_even = _mm_add_epi32(_mm_add_epi32(res_0, res_4),
+ _mm_add_epi32(res_2, res_6));
- // Combine results into one register.
- // We store the columns in the order 0, 2, 4, 6, 1, 3, 5, 7
- // as this order helps with the vertical filter.
- tmp[k + 7] = _mm_packs_epi32(res_even, res_odd);
- }
+ // Filter odd-index pixels
+ __m128i tmp_1 = filter[(sx + 1 * alpha) >> WARPEDDIFF_PREC_BITS];
+ __m128i tmp_3 = filter[(sx + 3 * alpha) >> WARPEDDIFF_PREC_BITS];
+ __m128i tmp_5 = filter[(sx + 5 * alpha) >> WARPEDDIFF_PREC_BITS];
+ __m128i tmp_7 = filter[(sx + 7 * alpha) >> WARPEDDIFF_PREC_BITS];
+
+ __m128i tmp_9 = _mm_unpacklo_epi32(tmp_1, tmp_3);
+ __m128i tmp_11 = _mm_unpacklo_epi32(tmp_5, tmp_7);
+ __m128i tmp_13 = _mm_unpackhi_epi32(tmp_1, tmp_3);
+ __m128i tmp_15 = _mm_unpackhi_epi32(tmp_5, tmp_7);
+
+ __m128i coeff_1 = _mm_unpacklo_epi64(tmp_9, tmp_11);
+ __m128i coeff_3 = _mm_unpackhi_epi64(tmp_9, tmp_11);
+ __m128i coeff_5 = _mm_unpacklo_epi64(tmp_13, tmp_15);
+ __m128i coeff_7 = _mm_unpackhi_epi64(tmp_13, tmp_15);
+
+ __m128i src_1 = _mm_unpacklo_epi8(_mm_srli_si128(src, 1), zero);
+ __m128i res_1 = _mm_madd_epi16(src_1, coeff_1);
+ __m128i src_3 = _mm_unpacklo_epi8(_mm_srli_si128(src, 3), zero);
+ __m128i res_3 = _mm_madd_epi16(src_3, coeff_3);
+ __m128i src_5 = _mm_unpacklo_epi8(_mm_srli_si128(src, 5), zero);
+ __m128i res_5 = _mm_madd_epi16(src_5, coeff_5);
+ __m128i src_7 = _mm_unpacklo_epi8(_mm_srli_si128(src, 7), zero);
+ __m128i res_7 = _mm_madd_epi16(src_7, coeff_7);
+
+ __m128i res_odd = _mm_add_epi32(_mm_add_epi32(res_1, res_5),
+ _mm_add_epi32(res_3, res_7));
+
+ // Combine results into one register.
+ // We store the columns in the order 0, 2, 4, 6, 1, 3, 5, 7
+ // as this order helps with the vertical filter.
+ tmp[k + 7] = _mm_packs_epi32(res_even, res_odd);
}
}
// Vertical filter
- for (k = -4; k < AOMMIN(4, p_row + p_height - i - 4); ++k) {
- int sy = sy4 + gamma * (-4) + delta * k;
+ for (k = -4; k < AOMMIN(4, p_height - i - 4); ++k) {
+ int sy = sy4 + gamma * (-4) + delta * k +
+ (1 << (WARPEDDIFF_PREC_BITS - 1)) +
+ (WARPEDPIXEL_PREC_SHIFTS << WARPEDDIFF_PREC_BITS);
- // Load per-pixel coefficients
- __m128i coeffs[8];
- for (l = 0; l < 8; ++l) {
- coeffs[l] = ((__m128i *)warped_filter)[ROUND_POWER_OF_TWO_SIGNED(
- sy, WARPEDDIFF_PREC_BITS) +
- WARPEDPIXEL_PREC_SHIFTS];
- sy += gamma;
- }
+ // Load from tmp and rearrange pairs of consecutive rows into the
+ // column order 0 0 2 2 4 4 6 6; 1 1 3 3 5 5 7 7
+ __m128i *src = tmp + (k + 4);
+ __m128i src_0 = _mm_unpacklo_epi16(src[0], src[1]);
+ __m128i src_2 = _mm_unpacklo_epi16(src[2], src[3]);
+ __m128i src_4 = _mm_unpacklo_epi16(src[4], src[5]);
+ __m128i src_6 = _mm_unpacklo_epi16(src[6], src[7]);
- {
- // Rearrange coefficients in the same way as for the horizontal filter
- __m128i tmp_0 = _mm_unpacklo_epi32(coeffs[0], coeffs[2]);
- __m128i tmp_1 = _mm_unpacklo_epi32(coeffs[1], coeffs[3]);
- __m128i tmp_2 = _mm_unpacklo_epi32(coeffs[4], coeffs[6]);
- __m128i tmp_3 = _mm_unpacklo_epi32(coeffs[5], coeffs[7]);
- __m128i tmp_4 = _mm_unpackhi_epi32(coeffs[0], coeffs[2]);
- __m128i tmp_5 = _mm_unpackhi_epi32(coeffs[1], coeffs[3]);
- __m128i tmp_6 = _mm_unpackhi_epi32(coeffs[4], coeffs[6]);
- __m128i tmp_7 = _mm_unpackhi_epi32(coeffs[5], coeffs[7]);
+ // Filter even-index pixels
+ __m128i tmp_0 = filter[(sy + 0 * gamma) >> WARPEDDIFF_PREC_BITS];
+ __m128i tmp_2 = filter[(sy + 2 * gamma) >> WARPEDDIFF_PREC_BITS];
+ __m128i tmp_4 = filter[(sy + 4 * gamma) >> WARPEDDIFF_PREC_BITS];
+ __m128i tmp_6 = filter[(sy + 6 * gamma) >> WARPEDDIFF_PREC_BITS];
- __m128i coeff_a = _mm_unpacklo_epi64(tmp_0, tmp_2);
- __m128i coeff_b = _mm_unpacklo_epi64(tmp_1, tmp_3);
- __m128i coeff_c = _mm_unpackhi_epi64(tmp_0, tmp_2);
- __m128i coeff_d = _mm_unpackhi_epi64(tmp_1, tmp_3);
- __m128i coeff_e = _mm_unpacklo_epi64(tmp_4, tmp_6);
- __m128i coeff_f = _mm_unpacklo_epi64(tmp_5, tmp_7);
- __m128i coeff_g = _mm_unpackhi_epi64(tmp_4, tmp_6);
- __m128i coeff_h = _mm_unpackhi_epi64(tmp_5, tmp_7);
+ __m128i tmp_8 = _mm_unpacklo_epi32(tmp_0, tmp_2);
+ __m128i tmp_10 = _mm_unpacklo_epi32(tmp_4, tmp_6);
+ __m128i tmp_12 = _mm_unpackhi_epi32(tmp_0, tmp_2);
+ __m128i tmp_14 = _mm_unpackhi_epi32(tmp_4, tmp_6);
- // Load from tmp and rearrange pairs of consecutive rows into the
- // column order 0 0 2 2 4 4 6 6; 1 1 3 3 5 5 7 7
- __m128i *src = tmp + (k + 4);
- __m128i src_a = _mm_unpacklo_epi16(src[0], src[1]);
- __m128i src_b = _mm_unpackhi_epi16(src[0], src[1]);
- __m128i src_c = _mm_unpacklo_epi16(src[2], src[3]);
- __m128i src_d = _mm_unpackhi_epi16(src[2], src[3]);
- __m128i src_e = _mm_unpacklo_epi16(src[4], src[5]);
- __m128i src_f = _mm_unpackhi_epi16(src[4], src[5]);
- __m128i src_g = _mm_unpacklo_epi16(src[6], src[7]);
- __m128i src_h = _mm_unpackhi_epi16(src[6], src[7]);
+ __m128i coeff_0 = _mm_unpacklo_epi64(tmp_8, tmp_10);
+ __m128i coeff_2 = _mm_unpackhi_epi64(tmp_8, tmp_10);
+ __m128i coeff_4 = _mm_unpacklo_epi64(tmp_12, tmp_14);
+ __m128i coeff_6 = _mm_unpackhi_epi64(tmp_12, tmp_14);
- // Calculate the filtered pixels
- __m128i res_a = _mm_madd_epi16(src_a, coeff_a);
- __m128i res_b = _mm_madd_epi16(src_b, coeff_b);
- __m128i res_c = _mm_madd_epi16(src_c, coeff_c);
- __m128i res_d = _mm_madd_epi16(src_d, coeff_d);
- __m128i res_e = _mm_madd_epi16(src_e, coeff_e);
- __m128i res_f = _mm_madd_epi16(src_f, coeff_f);
- __m128i res_g = _mm_madd_epi16(src_g, coeff_g);
- __m128i res_h = _mm_madd_epi16(src_h, coeff_h);
+ __m128i res_0 = _mm_madd_epi16(src_0, coeff_0);
+ __m128i res_2 = _mm_madd_epi16(src_2, coeff_2);
+ __m128i res_4 = _mm_madd_epi16(src_4, coeff_4);
+ __m128i res_6 = _mm_madd_epi16(src_6, coeff_6);
- __m128i res_even = _mm_add_epi32(_mm_add_epi32(res_a, res_c),
- _mm_add_epi32(res_e, res_g));
- __m128i res_odd = _mm_add_epi32(_mm_add_epi32(res_b, res_d),
- _mm_add_epi32(res_f, res_h));
+ __m128i res_even = _mm_add_epi32(_mm_add_epi32(res_0, res_2),
+ _mm_add_epi32(res_4, res_6));
- // Round and pack down to pixels
- __m128i res_lo = _mm_unpacklo_epi32(res_even, res_odd);
- __m128i res_hi = _mm_unpackhi_epi32(res_even, res_odd);
+ // Filter odd-index pixels
+ __m128i src_1 = _mm_unpackhi_epi16(src[0], src[1]);
+ __m128i src_3 = _mm_unpackhi_epi16(src[2], src[3]);
+ __m128i src_5 = _mm_unpackhi_epi16(src[4], src[5]);
+ __m128i src_7 = _mm_unpackhi_epi16(src[6], src[7]);
- __m128i round_const =
- _mm_set1_epi32((1 << (2 * WARPEDPIXEL_FILTER_BITS)) >> 1);
+ __m128i tmp_1 = filter[(sy + 1 * gamma) >> WARPEDDIFF_PREC_BITS];
+ __m128i tmp_3 = filter[(sy + 3 * gamma) >> WARPEDDIFF_PREC_BITS];
+ __m128i tmp_5 = filter[(sy + 5 * gamma) >> WARPEDDIFF_PREC_BITS];
+ __m128i tmp_7 = filter[(sy + 7 * gamma) >> WARPEDDIFF_PREC_BITS];
- __m128i res_lo_round = _mm_srai_epi32(
- _mm_add_epi32(res_lo, round_const), 2 * WARPEDPIXEL_FILTER_BITS);
- __m128i res_hi_round = _mm_srai_epi32(
- _mm_add_epi32(res_hi, round_const), 2 * WARPEDPIXEL_FILTER_BITS);
+ __m128i tmp_9 = _mm_unpacklo_epi32(tmp_1, tmp_3);
+ __m128i tmp_11 = _mm_unpacklo_epi32(tmp_5, tmp_7);
+ __m128i tmp_13 = _mm_unpackhi_epi32(tmp_1, tmp_3);
+ __m128i tmp_15 = _mm_unpackhi_epi32(tmp_5, tmp_7);
- __m128i res_16bit = _mm_packs_epi32(res_lo_round, res_hi_round);
- __m128i res_8bit = _mm_packus_epi16(res_16bit, res_16bit);
+ __m128i coeff_1 = _mm_unpacklo_epi64(tmp_9, tmp_11);
+ __m128i coeff_3 = _mm_unpackhi_epi64(tmp_9, tmp_11);
+ __m128i coeff_5 = _mm_unpacklo_epi64(tmp_13, tmp_15);
+ __m128i coeff_7 = _mm_unpackhi_epi64(tmp_13, tmp_15);
- // Store, blending with 'pred' if needed
- __m128i *p =
- (__m128i *)&pred[(i - p_row + k + 4) * p_stride + (j - p_col)];
+ __m128i res_1 = _mm_madd_epi16(src_1, coeff_1);
+ __m128i res_3 = _mm_madd_epi16(src_3, coeff_3);
+ __m128i res_5 = _mm_madd_epi16(src_5, coeff_5);
+ __m128i res_7 = _mm_madd_epi16(src_7, coeff_7);
- if (ref_frm) {
- __m128i orig = _mm_loadl_epi64(p);
- _mm_storel_epi64(p, _mm_avg_epu8(res_8bit, orig));
- } else {
- _mm_storel_epi64(p, res_8bit);
- }
+ __m128i res_odd = _mm_add_epi32(_mm_add_epi32(res_1, res_3),
+ _mm_add_epi32(res_5, res_7));
+
+ // Rearrange pixels back into the order 0 ... 7
+ __m128i res_lo = _mm_unpacklo_epi32(res_even, res_odd);
+ __m128i res_hi = _mm_unpackhi_epi32(res_even, res_odd);
+
+ // Round and pack into 8 bits
+ __m128i round_const =
+ _mm_set1_epi32((1 << (2 * WARPEDPIXEL_FILTER_BITS)) >> 1);
+
+ __m128i res_lo_round = _mm_srai_epi32(
+ _mm_add_epi32(res_lo, round_const), 2 * WARPEDPIXEL_FILTER_BITS);
+ __m128i res_hi_round = _mm_srai_epi32(
+ _mm_add_epi32(res_hi, round_const), 2 * WARPEDPIXEL_FILTER_BITS);
+
+ __m128i res_16bit = _mm_packs_epi32(res_lo_round, res_hi_round);
+ __m128i res_8bit = _mm_packus_epi16(res_16bit, res_16bit);
+
+ // Store, blending with 'pred' if needed
+ __m128i *p = (__m128i *)&pred[(i + k + 4) * p_stride + j];
+
+ if (ref_frm) {
+ __m128i orig = _mm_loadl_epi64(p);
+ _mm_storel_epi64(p, _mm_avg_epu8(res_8bit, orig));
+ } else {
+ _mm_storel_epi64(p, res_8bit);
}
}
}