// These instructions multiply a 2x8 matrix (samples) by an 8x2 matrix // (filter), destructively accumulating into the destination register.
int32x4_t sum = vusmmlaq_s32(horiz_const, perm_samples[0], filter[0]);
sum = vusmmlaq_s32(sum, perm_samples[1], filter[1]);
static inline void convolve_x_sr_12tap_neon_i8mm(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);
// Split 12-tap filter into two 6-tap filters, masking the top two elements. // { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0 } const int8x8_t mask = vcreate_s8(0x0000ffffffffffff); const int8x8_t filter_0 = vand_s8(vmovn_s16(vld1q_s16(x_filter_ptr)), mask); const int8x8_t filter_1 =
vext_s8(vmovn_s16(vld1q_s16(x_filter_ptr + 4)), vdup_n_s8(0), 2);
// A shim of 1 << (ROUND0_BITS - 1) enables us to simplify computation in the // convolution kernels: Adding this shim enables us to use a single rounding // right shift by FILTER_BITS instead of two rounding right shifts: first by // ROUND0_BITS, and then subsequently by FILTER_BITS - ROUND0_BITS. const int32x4_t horiz_const = vdupq_n_s32(1 << (ROUND0_BITS - 1));
if (w <= 4) { const uint8x16_t permute_tbl = vld1q_u8(kMatMul6PermuteTbl);
// These instructions multiply a 2x8 matrix (samples) by an 8x2 matrix // (filter), destructively accumulating into the destination register.
int32x4_t sum = vusmmlaq_s32(horiz_const, perm_samples, filter);
// Further narrowing and packing is performed by the caller. return vmovn_s32(sum);
}
// These instructions multiply a 2x8 matrix (samples) by an 8x2 matrix // (filter), destructively accumulating into the destination register.
int32x4_t sum0123 = vusmmlaq_s32(horiz_const, perm_samples[0], filter);
int32x4_t sum4567 = vusmmlaq_s32(horiz_const, perm_samples[1], filter);
int16x8_t sum = 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, FILTER_BITS - 1);
}
static inline void convolve_x_sr_6tap_neon_i8mm( 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 int32x4_t horiz_const) { // Filter values are even, so halve to reduce intermediate precision reqs. const int8x8_t x_filter_s8 = vshrn_n_s16(vld1q_s16(filter_x), 1); // Stagger the filter for use with the matrix multiply instructions. // { f0, f1, f2, f3, f4, f5, 0, 0, 0, f0, f1, f2, f3, f4, f5, 0 } const int8x16_t x_filter =
vcombine_s8(vext_s8(x_filter_s8, x_filter_s8, 1), x_filter_s8);
int filter_taps = get_filter_tap(filter_params_x, subpel_x_qn & SUBPEL_MASK);
// A shim of 1 << (ROUND0_BITS - 1) enables us to simplify computation in the // convolution kernels: Adding this shim enables us to use a single rounding // right shift by FILTER_BITS instead of two rounding right shifts: first by // ROUND0_BITS, and then subsequently by FILTER_BITS - ROUND0_BITS. // Halve the total because we will halve the filter values. const int32x4_t horiz_const_s32 = vdupq_n_s32(1 << (ROUND0_BITS - 1) / 2); const int16x8_t horiz_const_s16 = vdupq_n_s16(1 << (ROUND0_BITS - 1) / 2);
// 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_i8mm(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);
// 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_i8mm(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;
// 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_i8mm(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);
uint8x16x2_t samples_LUT;
// This operation combines a conventional transpose and the sample permute // required before computing the dot product.
uint8x16_t s0123;
transpose_concat_elems_u8_4x4(s0, s1, s2, s3, &s0123);
// This operation combines a conventional transpose and the sample permute // required before computing the dot product.
uint8x16_t s0123_lo, s0123_hi;
transpose_concat_elems_u8_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_i8mm(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;
}
// Apply tap 0 and accumulate.
sum = vreinterpretq_s16_u16(
vmlsl_u8(vreinterpretq_u16_s16(sum), vget_low_u8(samples), f0));
sum = vaddq_s16(sum, horiz_const);
// We halved the convolution filter values so -1 from the right shift. return vshrq_n_s16(sum, ROUND0_BITS - 1);
}
static inline void convolve_2d_sr_horiz_8tap_neon_i8mm( 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) { // Filter values are even, so halve to reduce intermediate precision reqs. const int8x8_t x_filter_s8 = vshrn_n_s16(vld1q_s16(x_filter_ptr), 1);
// Stagger the filter for use with the matrix multiply instructions. // { f1, f2, f3, f4, f5, f6, f7, 0, 0, f1, f2, f3, f4, f5, f6, f7 } const uint8x16_t filter_idx = vld1q_u8(kFilterPermuteTbl); const int8x16_t x_filter =
vqtbl1q_s8(vcombine_s8(x_filter_s8, vdup_n_s8(0)), filter_idx);
// Since f0 is always negative and s0 is unsigned, subtract (unsigned) s0 * // -f0 to avoid signed overflow. const uint8x8_t f0 = vdup_n_u8(-x_filter_ptr[0] >> 1); const uint8x16x2_t permute_tbl = vld1q_u8_x2(kMatMul8PermuteTbl);
const int bd = 8; // This shim of 1 << ((ROUND0_BITS - 1) - 1) enables us to use non-rounding // shifts - which are generally faster than rounding shifts on modern CPUs. // The outermost -1 is needed because we halved the filter values. const int16x8_t horiz_const = vdupq_n_s16((1 << (bd + FILTER_BITS - 2)) +
(1 << ((ROUND0_BITS - 1) - 1)));
const uint8_t *src_ptr = src;
int16_t *dst_ptr = im_block;
int dst_stride = im_stride;
int height = im_h;
do { const uint8_t *s = src_ptr;
int16_t *d = dst_ptr;
int width = w;
// 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_i8mm( const uint8_t *src, int src_stride, int16_t *dst, int dst_stride, int width,
int height, 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. // Halve the total because we halved the filter values. const int32x4_t horiz_const = vdupq_n_s32(
(((1 << (bd + FILTER_BITS - 1)) + (1 << (ROUND0_BITS - 1))) / 2));
// These instructions multiply a 2x8 matrix (samples) by an 8x2 matrix // (filter), destructively accumulating into the destination register.
int32x4_t sum = vusmmlaq_s32(horiz_const, perm_samples, filter);
// We halved the convolution filter values so -1 from the right shift. return vshrn_n_s32(sum, ROUND0_BITS - 1);
}
// These instructions multiply a 2x8 matrix (samples) by an 8x2 matrix // (filter), destructively accumulating into the destination register.
int32x4_t sum0123 = vusmmlaq_s32(horiz_const, perm_samples[0], filter);
int32x4_t sum4567 = vusmmlaq_s32(horiz_const, perm_samples[1], filter);
// 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_6tap_neon_i8mm(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_s8 = vshrn_n_s16(vld1q_s16(x_filter_ptr), 1); // Stagger the filter for use with the matrix multiply instructions. // { f0, f1, f2, f3, f4, f5, 0, 0, 0, f0, f1, f2, f3, f4, f5, 0 } const int8x16_t x_filter =
vcombine_s8(vext_s8(x_filter_s8, x_filter_s8, 1), x_filter_s8);
const int bd = 8; // This shim of 1 << ((ROUND0_BITS - 1) - 1) enables us to use non-rounding // shifts in convolution kernels - which are generally faster than rounding // shifts on modern CPUs. The outermost -1 is needed because we halved the // filter values. const int32x4_t horiz_const = vdupq_n_s32((1 << (bd + FILTER_BITS - 2)) +
(1 << ((ROUND0_BITS - 1) - 1))); const int16x8_t vert_const = vdupq_n_s16(1 << (bd - 1)); const uint8x16x2_t permute_tbl = vld1q_u8_x2(kMatMul6PermuteTbl);
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_6tap_4tap_neon_i8mm( 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 int16x4_t y_filter = vld1_s16(y_filter_ptr + 2); // Filter values are even, so halve to reduce intermediate precision reqs. const int8x8_t x_filter_s8 = vshrn_n_s16(vld1q_s16(x_filter_ptr), 1); // Stagger the filter for use with the matrix multiply instructions. // { f0, f1, f2, f3, f4, f5, 0, 0, 0, f0, f1, f2, f3, f4, f5, 0 } const int8x16_t x_filter =
vcombine_s8(vext_s8(x_filter_s8, x_filter_s8, 1), x_filter_s8);
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. // Halve the total because we halved the filter values. const int32x4_t horiz_const = vdupq_n_s32(
((1 << (bd + FILTER_BITS - 1)) + (1 << (ROUND0_BITS - 1))) / 2); const int16x8_t vert_const = vdupq_n_s16(1 << (bd - 1));
if (w == 4) { const uint8x16_t permute_tbl = vld1q_u8(kMatMul6PermuteTbl);
uint8x16_t h_s0, h_s1, h_s2;
load_u8_16x3(src, src_stride, &h_s0, &h_s1, &h_s2);
// Used for both 6, 4 and 4, 4 horiz, vert filter tap combinations.
if (x_filter_taps <= 6 && y_filter_taps <= 4) {
convolve_2d_sr_6tap_4tap_neon_i8mm(src_ptr + 1, src_stride, dst,
dst_stride, w, h, x_filter_ptr,
y_filter_ptr); return;
}
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.