// Dot product constants: // Accumulate into 128 << FILTER_BITS to account for range transform. // Adding a shim of 1 << (ROUND0_BITS - 1) enables us to use a single rounding // right shift by FILTER_BITS - instead of a first rounding right shift by // ROUND0_BITS, followed by second rounding right shift by FILTER_BITS - // ROUND0_BITS.
int32x4_t acc =
vdupq_n_s32((128 << FILTER_BITS) + (1 << ((ROUND0_BITS - 1))));
int32x4_t sum = vdotq_laneq_s32(acc, perm_samples[0], filter, 0);
sum = vdotq_laneq_s32(sum, perm_samples[1], filter, 1);
sum = vdotq_laneq_s32(sum, perm_samples[2], filter, 2);
return vshrn_n_s32(sum, 1);
}
static inline uint8x8_t convolve12_8_x(uint8x16_t samples[2], const int8x16_t filter, const uint8x16x3_t permute_tbl) { // Transform sample range to [-128, 127] for 8-bit signed dot product.
int8x16_t samples_128[2] = {
vreinterpretq_s8_u8(vsubq_u8(samples[0], vdupq_n_u8(128))),
vreinterpretq_s8_u8(vsubq_u8(samples[1], vdupq_n_u8(128)))
};
// Dot product constants: // Accumulate into 128 << FILTER_BITS to account for range transform. // Adding a shim of 1 << (ROUND0_BITS - 1) enables us to use a single rounding // right shift by FILTER_BITS - instead of a first rounding right shift by // ROUND0_BITS, followed by second rounding right shift by FILTER_BITS - // ROUND0_BITS.
int32x4_t acc =
vdupq_n_s32((128 << FILTER_BITS) + (1 << ((ROUND0_BITS - 1))));
static inline void convolve_x_sr_12tap_neon_dotprod( const uint8_t *src, int src_stride, uint8_t *dst, int dst_stride, int w,
int h, const int16_t *x_filter_ptr) { // The no-op filter should never be used here.
assert(x_filter_ptr[5] != 128);
// Dot product constants: // Accumulate into 128 << FILTER_BITS to account for range transform. // Adding a shim of 1 << (ROUND0_BITS - 1) enables us to use a single rounding // right shift by FILTER_BITS - instead of a first rounding right shift by // ROUND0_BITS, followed by second rounding right shift by FILTER_BITS - // ROUND0_BITS. Halve the total because we halved the filter values.
int32x4_t acc =
vdupq_n_s32(((128 << FILTER_BITS) + (1 << ((ROUND0_BITS - 1)))) / 2);
int32x4_t sum = vdotq_lane_s32(acc, perm_samples, filters, 0);
// Further narrowing and packing is performed by the caller. return vmovn_s32(sum);
}
static inline uint8x8_t convolve4_8_x(const uint8x16_t samples, const int8x8_t filters, const uint8x16x2_t permute_tbl) { // Transform sample range to [-128, 127] for 8-bit signed dot product.
int8x16_t samples_128 =
vreinterpretq_s8_u8(vsubq_u8(samples, vdupq_n_u8(128)));
// Dot product constants: // Accumulate into 128 << FILTER_BITS to account for range transform. // Adding a shim of 1 << (ROUND0_BITS - 1) enables us to use a single rounding // right shift by FILTER_BITS - instead of a first rounding right shift by // ROUND0_BITS, followed by second rounding right shift by FILTER_BITS - // ROUND0_BITS. Halve the total because we halved the filter values.
int32x4_t acc =
vdupq_n_s32(((128 << FILTER_BITS) + (1 << ((ROUND0_BITS - 1)))) / 2);
// Narrow and re-pack.
int16x8_t sum = vcombine_s16(vmovn_s32(sum0123), vmovn_s32(sum4567)); // We halved the filter values so -1 from right shift. return vqrshrun_n_s16(sum, FILTER_BITS - 1);
}
static inline void convolve_x_sr_4tap_neon_dotprod( const uint8_t *src, ptrdiff_t src_stride, uint8_t *dst,
ptrdiff_t dst_stride, int width, int height, const int16_t *filter_x) { const int16x4_t x_filter = vld1_s16(filter_x + 2); // All 4-tap and bilinear filter values are even, so halve them to reduce // intermediate precision requirements. const int8x8_t filter = vshrn_n_s16(vcombine_s16(x_filter, vdup_n_s16(0)), 1);
if (width == 4) { const uint8x16_t permute_tbl = vld1q_u8(kDotProdPermuteTbl);
// Dot product constants: // Accumulate into 128 << FILTER_BITS to account for range transform. // Adding a shim of 1 << (ROUND0_BITS - 1) enables us to use a single rounding // right shift by FILTER_BITS - instead of a first rounding right shift by // ROUND0_BITS, followed by second rounding right shift by FILTER_BITS - // ROUND0_BITS. Halve the total because we halved the filter values.
int32x4_t acc =
vdupq_n_s32(((128 << FILTER_BITS) + (1 << ((ROUND0_BITS - 1)))) / 2);
// Narrow and re-pack.
int16x8_t sum_s16 = vcombine_s16(vmovn_s32(sum0123), vmovn_s32(sum4567)); // We halved the convolution filter values so - 1 from the right shift. return vqrshrun_n_s16(sum_s16, FILTER_BITS - 1);
}
void av1_convolve_x_sr_neon_dotprod(const uint8_t *src, int src_stride,
uint8_t *dst, int dst_stride, int w, int h, const InterpFilterParams *filter_params_x, const int subpel_x_qn,
ConvolveParams *conv_params) {
if (w == 2 || h == 2) {
av1_convolve_x_sr_c(src, src_stride, dst, dst_stride, w, h, filter_params_x,
subpel_x_qn, conv_params); return;
}
s += 8;
d += 8;
width -= 8;
} while (width != 0);
src += 4 * src_stride;
dst += 4 * dst_stride;
h -= 4;
} while (h != 0);
}
static inline int16x4_t convolve12_4_y(const int8x16_t s0, const int8x16_t s1, const int8x16_t s2, const int8x8_t filters_0_7, const int8x8_t filters_4_11) { // The sample range transform and permutation are performed by the caller. // Accumulate into 128 << FILTER_BITS to account for range transform. const int32x4_t acc = vdupq_n_s32(128 << FILTER_BITS);
int32x4_t sum = vdotq_lane_s32(acc, s0, filters_0_7, 0);
sum = vdotq_lane_s32(sum, s1, filters_0_7, 1);
sum = vdotq_lane_s32(sum, s2, filters_4_11, 1);
// Further narrowing and packing is performed by the caller. return vshrn_n_s32(sum, 1);
}
static inline uint8x8_t convolve12_8_y( const int8x16_t s0_lo, const int8x16_t s0_hi, const int8x16_t s1_lo, const int8x16_t s1_hi, const int8x16_t s2_lo, const int8x16_t s2_hi, const int8x8_t filters_0_7, const int8x8_t filters_4_11) { // The sample range transform and permutation are performed by the caller. // Accumulate into 128 << FILTER_BITS to account for range transform. const int32x4_t acc = vdupq_n_s32(128 << FILTER_BITS);
// Narrow and re-pack.
int16x8_t sum =
vcombine_s16(vshrn_n_s32(sum0123, 1), vshrn_n_s32(sum4567, 1)); return vqrshrun_n_s16(sum, FILTER_BITS - 1);
}
static inline void convolve_y_sr_12tap_neon_dotprod( const uint8_t *src_ptr, int src_stride, uint8_t *dst_ptr, int dst_stride,
int w, int h, const int16_t *y_filter_ptr) { // The no-op filter should never be used here.
assert(y_filter_ptr[5] != 128);
// Prepare block for next iteration - re-using as much as possible. // Shuffle everything up four rows.
s0123_lo = s4567_lo;
s0123_hi = s4567_hi;
s1234_lo = s5678_lo;
s1234_hi = s5678_hi;
s2345_lo = s6789_lo;
s2345_hi = s6789_hi;
s3456_lo = s789A_lo;
s3456_hi = s789A_hi;
s4567_lo = s89AB_lo;
s4567_hi = s89AB_hi;
s5678_lo = s9ABC_lo;
s5678_hi = s9ABC_hi;
s6789_lo = sABCD_lo;
s6789_hi = sABCD_hi;
s789A_lo = sBCDE_lo;
s789A_hi = sBCDE_hi;
s += 4 * src_stride;
d += 4 * dst_stride;
height -= 4;
} while (height != 0);
src_ptr += 8;
dst_ptr += 8;
w -= 8;
} while (w != 0);
}
}
static inline int16x4_t convolve8_4_y(const int8x16_t s0, const int8x16_t s1, const int8x8_t filters) { // The sample range transform and permutation are performed by the caller. // Accumulate into 128 << FILTER_BITS to account for range transform. // (- 1 since we halved the filters.) const int32x4_t acc = vdupq_n_s32(128 << (FILTER_BITS - 1));
int32x4_t sum = vdotq_lane_s32(acc, s0, filters, 0);
sum = vdotq_lane_s32(sum, s1, filters, 1);
// Further narrowing and packing is performed by the caller. return vmovn_s32(sum);
}
static inline uint8x8_t convolve8_8_y(const int8x16_t s0_lo, const int8x16_t s0_hi, const int8x16_t s1_lo, const int8x16_t s1_hi, const int8x8_t filters) { // The sample range transform and permutation are performed by the caller. // Accumulate into 128 << FILTER_BITS to account for range transform. // (- 1 since we halved the filters.) const int32x4_t acc = vdupq_n_s32(128 << (FILTER_BITS - 1));
// Narrow and re-pack.
int16x8_t sum = vcombine_s16(vmovn_s32(sum0123), vmovn_s32(sum4567)); // We halved the filter values so -1 from right shift. return vqrshrun_n_s16(sum, FILTER_BITS - 1);
}
static inline void convolve_y_sr_8tap_neon_dotprod( const uint8_t *src_ptr, int src_stride, uint8_t *dst_ptr, int dst_stride,
int w, int h, const int16_t *y_filter_ptr) { // Filter values are even, so halve to reduce intermediate precision reqs. const int8x8_t filter = vshrn_n_s16(vld1q_s16(y_filter_ptr), 1);
// Prepare block for next iteration - re-using as much as possible. // Shuffle everything up four rows.
s0123 = s4567;
s1234 = s5678;
s2345 = s6789;
s3456 = s789A;
src_ptr += 4 * src_stride;
dst_ptr += 4 * dst_stride;
h -= 4;
} while (h != 0);
} else { do {
int height = h; const uint8_t *s = src_ptr;
uint8_t *d = dst_ptr;
// Prepare block for next iteration - re-using as much as possible. // Shuffle everything up four rows.
s0123_lo = s4567_lo;
s0123_hi = s4567_hi;
s1234_lo = s5678_lo;
s1234_hi = s5678_hi;
s2345_lo = s6789_lo;
s2345_hi = s6789_hi;
s3456_lo = s789A_lo;
s3456_hi = s789A_hi;
s += 4 * src_stride;
d += 4 * dst_stride;
height -= 4;
} while (height != 0);
src_ptr += 8;
dst_ptr += 8;
w -= 8;
} while (w != 0);
}
}
static inline int16x4_t convolve4_4_y(const int8x16_t s0, const int8x8_t filters) { // The sample range transform and permutation are performed by the caller. // Accumulate into 128 << FILTER_BITS to account for range transform. // (- 1 since we halved the filters.) const int32x4_t acc = vdupq_n_s32(128 << (FILTER_BITS - 1));
int32x4_t sum = vdotq_lane_s32(acc, s0, filters, 0);
// Further narrowing and packing is performed by the caller. return vmovn_s32(sum);
}
static inline uint8x8_t convolve4_8_y(const int8x16_t s0, const int8x16_t s1, const int8x8_t filters) { // The sample range transform and permutation are performed by the caller. // Accumulate into 128 << FILTER_BITS to account for range transform. // (- 1 since we halved the filters.) const int32x4_t acc = vdupq_n_s32(128 << (FILTER_BITS - 1));
// Narrow and re-pack.
int16x8_t sum = vcombine_s16(vmovn_s32(sum0123), vmovn_s32(sum4567)); // We halved the filter values so -1 from right shift. return vqrshrun_n_s16(sum, FILTER_BITS - 1);
}
static inline void convolve_y_sr_4tap_neon_dotprod( const uint8_t *src_ptr, int src_stride, uint8_t *dst_ptr, int dst_stride,
int w, int h, const int16_t *y_filter_ptr) { // Filter values are even, so halve to reduce intermediate precision reqs. const int16x8_t filter_s16 =
vcombine_s16(vld1_s16(y_filter_ptr + 2), vdup_n_s16(0)); const int8x8_t filter = vshrn_n_s16(filter_s16, 1); const uint8x16x3_t merge_block_tbl = vld1q_u8_x3(kDotProdMergeBlockTbl);
int8x16x2_t samples_LUT;
// Transform sample range to [-128, 127] for 8-bit signed dot product.
int8x8_t s0 = vreinterpret_s8_u8(vsub_u8(t0, vdup_n_u8(128)));
int8x8_t s1 = vreinterpret_s8_u8(vsub_u8(t1, vdup_n_u8(128)));
int8x8_t s2 = vreinterpret_s8_u8(vsub_u8(t2, vdup_n_u8(128)));
int8x8_t s3 = vreinterpret_s8_u8(vsub_u8(t3, vdup_n_u8(128)));
// This operation combines a conventional transpose and the sample permute // required before computing the dot product.
int8x16_t s0123;
transpose_concat_elems_s8_4x4(s0, s1, s2, s3, &s0123);
// Transform sample range to [-128, 127] for 8-bit signed dot product.
int8x8_t s0 = vreinterpret_s8_u8(vsub_u8(t0, vdup_n_u8(128)));
int8x8_t s1 = vreinterpret_s8_u8(vsub_u8(t1, vdup_n_u8(128)));
int8x8_t s2 = vreinterpret_s8_u8(vsub_u8(t2, vdup_n_u8(128)));
int8x8_t s3 = vreinterpret_s8_u8(vsub_u8(t3, vdup_n_u8(128)));
// This operation combines a conventional transpose and the sample permute // required before computing the dot product.
int8x16_t s0123_lo, s0123_hi;
transpose_concat_elems_s8_8x4(s0, s1, s2, s3, &s0123_lo, &s0123_hi);
// Prepare block for next iteration - re-using as much as possible. // Shuffle everything up four rows.
s0123_lo = s4567_lo;
s0123_hi = s4567_hi;
s += 4 * src_stride;
d += 4 * dst_stride;
height -= 4;
} while (height != 0);
src_ptr += 8;
dst_ptr += 8;
w -= 8;
} while (w != 0);
}
}
void av1_convolve_y_sr_neon_dotprod(const uint8_t *src, int src_stride,
uint8_t *dst, int dst_stride, int w, int h, const InterpFilterParams *filter_params_y, const int subpel_y_qn) {
if (w == 2 || h == 2) {
av1_convolve_y_sr_c(src, src_stride, dst, dst_stride, w, h, filter_params_y,
subpel_y_qn); return;
}
// Accumulate dot product into 'correction' to account for range transform.
int32x4_t sum = vdotq_laneq_s32(horiz_const, perm_samples[0], filters, 0);
sum = vdotq_laneq_s32(sum, perm_samples[1], filters, 1);
sum = vdotq_laneq_s32(sum, perm_samples[2], filters, 2);
// Narrow and re-pack. return vshrn_n_s32(sum, ROUND0_BITS);
}
static inline int16x8_t convolve12_8_2d_h(uint8x16_t samples[2], const int8x16_t filters, const int32x4_t correction, const uint8x16x3_t permute_tbl) { // Transform sample range to [-128, 127] for 8-bit signed dot product.
int8x16_t samples_128[2] = {
vreinterpretq_s8_u8(vsubq_u8(samples[0], vdupq_n_u8(128))),
vreinterpretq_s8_u8(vsubq_u8(samples[1], vdupq_n_u8(128)))
};
// Narrow and re-pack. return vcombine_s16(vshrn_n_s32(sum0123, ROUND0_BITS),
vshrn_n_s32(sum4567, ROUND0_BITS));
}
static inline void convolve_2d_sr_horiz_12tap_neon_dotprod( const uint8_t *src_ptr, int src_stride, int16_t *dst_ptr, const int dst_stride, int w, int h, const int16x8_t x_filter_0_7, const int16x4_t x_filter_8_11) { // The no-op filter should never be used here.
assert(vgetq_lane_s16(x_filter_0_7, 5) != 128);
// Accumulate into 'correction' to account for range transform.
int32x4_t sum0123 = vdotq_lane_s32(correction, perm_samples[0], filters, 0);
int32x4_t sum4567 = vdotq_lane_s32(correction, perm_samples[1], filters, 0);
// Narrow and re-pack. // We halved the filter values so -1 from right shift. return vcombine_s16(vshrn_n_s32(sum0123, ROUND0_BITS - 1),
vshrn_n_s32(sum4567, ROUND0_BITS - 1));
}
static inline void convolve_2d_sr_horiz_4tap_neon_dotprod( const uint8_t *src, ptrdiff_t src_stride, int16_t *dst,
ptrdiff_t dst_stride, int w, int h, const int16_t *filter_x) { const int bd = 8; const int16x4_t x_filter = vld1_s16(filter_x + 2); // All 4-tap and bilinear filter values are even, so halve them to reduce // intermediate precision requirements. const int8x8_t filter = vshrn_n_s16(vcombine_s16(x_filter, vdup_n_s16(0)), 1);
// Adding a shim of 1 << (ROUND0_BITS - 1) enables us to use non-rounding // shifts - which are generally faster than rounding shifts on modern CPUs. const int32_t horiz_const =
((1 << (bd + FILTER_BITS - 1)) + (1 << (ROUND0_BITS - 1))); // Accumulate into 128 << FILTER_BITS to account for range transform. // Halve the total because we halved the filter values. const int32x4_t correction =
vdupq_n_s32(((128 << FILTER_BITS) + horiz_const) / 2);
if (w == 4) { const uint8x16_t permute_tbl = vld1q_u8(kDotProdPermuteTbl);
// Narrow and re-pack. // We halved the convolution filter values so -1 from the right shift. return vcombine_s16(vshrn_n_s32(sum0123, ROUND0_BITS - 1),
vshrn_n_s32(sum4567, ROUND0_BITS - 1));
}
static inline void convolve_2d_sr_horiz_8tap_neon_dotprod( const uint8_t *src, int src_stride, int16_t *im_block, int im_stride, int w,
int im_h, const int16_t *x_filter_ptr) { const int16x8_t x_filter_s16 = vld1q_s16(x_filter_ptr); // Filter values are even, so halve to reduce intermediate precision reqs. const int8x8_t x_filter = vshrn_n_s16(x_filter_s16, 1);
const int bd = 8; // Adding a shim of 1 << (ROUND0_BITS - 1) enables us to use non-rounding // shifts - which are generally faster than rounding shifts on modern CPUs. const int32_t horiz_const =
((1 << (bd + FILTER_BITS - 1)) + (1 << (ROUND0_BITS - 1))); // Halve the total because we halved the filter values. const int32x4_t correction =
vdupq_n_s32(((128 << FILTER_BITS) + horiz_const) / 2);
const uint8_t *src_ptr = src;
int16_t *dst_ptr = im_block;
int dst_stride = im_stride;
int height = im_h;
const uint8x16x3_t permute_tbl = vld1q_u8_x3(kDotProdPermuteTbl); do { const uint8_t *s = src_ptr;
int16_t *d = dst_ptr;
int width = w;
s += 8;
d += 8;
width -= 8;
} while (width != 0);
src_ptr += src_stride;
dst_ptr += dst_stride;
} while (--height != 0);
}
static inline void convolve_2d_sr_6tap_neon_dotprod( const uint8_t *src, int src_stride, uint8_t *dst, int dst_stride, int w,
int h, const int16_t *x_filter_ptr, const int16_t *y_filter_ptr) { const int16x8_t y_filter = vld1q_s16(y_filter_ptr); // Filter values are even, so halve to reduce intermediate precision reqs. const int8x8_t x_filter = vshrn_n_s16(vld1q_s16(x_filter_ptr), 1);
const int bd = 8; // Adding a shim of 1 << (ROUND0_BITS - 1) enables us to use non-rounding // shifts - which are generally faster than rounding shifts on modern CPUs. const int32_t horiz_const =
((1 << (bd + FILTER_BITS - 1)) + (1 << (ROUND0_BITS - 1))); // Accumulate into 128 << FILTER_BITS to account for range transform. // Halve the total because we halved the filter values. const int32x4_t correction =
vdupq_n_s32(((128 << FILTER_BITS) + horiz_const) / 2); const int16x8_t vert_const = vdupq_n_s16(1 << (bd - 1)); const uint8x16x3_t permute_tbl = vld1q_u8_x3(kDotProdPermuteTbl);
do { const uint8_t *s = src;
uint8_t *d = dst;
int height = h;
s += 4 * src_stride;
d += 4 * dst_stride;
height -= 4;
} while (height != 0);
src += 8;
dst += 8;
w -= 8;
} while (w != 0);
}
static inline void convolve_2d_sr_4tap_neon_dotprod( const uint8_t *src, int src_stride, uint8_t *dst, int dst_stride, int w,
int h, const int16_t *x_filter_ptr, const int16_t *y_filter_ptr) { const int bd = 8; const int16x8_t vert_const = vdupq_n_s16(1 << (bd - 1));
const int16x4_t y_filter = vld1_s16(y_filter_ptr + 2); const int16x4_t x_filter_s16 = vld1_s16(x_filter_ptr + 2); // All 4-tap and bilinear filter values are even, so halve them to reduce // intermediate precision requirements. const int8x8_t x_filter =
vshrn_n_s16(vcombine_s16(x_filter_s16, vdup_n_s16(0)), 1);
// Adding a shim of 1 << (ROUND0_BITS - 1) enables us to use non-rounding // shifts - which are generally faster than rounding shifts on modern CPUs. const int32_t horiz_const =
((1 << (bd + FILTER_BITS - 1)) + (1 << (ROUND0_BITS - 1))); // Accumulate into 128 << FILTER_BITS to account for range transform. // Halve the total because we halved the filter values. const int32x4_t correction =
vdupq_n_s32(((128 << FILTER_BITS) + horiz_const) / 2);
if (w == 4) { const uint8x16_t permute_tbl = vld1q_u8(kDotProdPermuteTbl);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.