staticvoid scale_plane_4_to_1_bilinear(const uint8_t *src, const ptrdiff_t src_stride,
uint8_t *dst, const ptrdiff_t dst_stride, const int dst_w, const int dst_h, const __m128i c0c1) { const int max_width = (dst_w + 15) & ~15;
int y = dst_h;
do {
int x = max_width; do {
__m128i s[8], d[8];
// Note: Using _mm_packus_epi32() in SSE4.1 could be faster. // Here we tried to not use shuffle instructions which would be slow // on some x86 CPUs.
// Horizontal // 000 001 xx xx 004 005 xx xx 008 009 xx xx 00C 00D xx xx // 010 011 xx xx 014 015 xx xx 018 019 xx xx 01C 01D xx xx // 020 021 xx xx 024 025 xx xx 028 029 xx xx 02C 02D xx xx // 030 031 xx xx 034 035 xx xx 038 039 xx xx 03C 03D xx xx // 100 101 xx xx 104 105 xx xx 108 109 xx xx 10C 10D xx xx // 110 111 xx xx 114 115 xx xx 118 119 xx xx 11C 11D xx xx // 120 121 xx xx 124 125 xx xx 128 129 xx xx 12C 12D xx xx // 130 131 xx xx 134 135 xx xx 138 139 xx xx 13C 13D xx xx
s[0] = _mm_loadu_si128((const __m128i *)(&src[0]));
s[1] = _mm_loadu_si128((const __m128i *)(&src[16]));
s[2] = _mm_loadu_si128((const __m128i *)(&src[32]));
s[3] = _mm_loadu_si128((const __m128i *)(&src[48]));
s[4] = _mm_loadu_si128((const __m128i *)(src + src_stride + 0));
s[5] = _mm_loadu_si128((const __m128i *)(src + src_stride + 16));
s[6] = _mm_loadu_si128((const __m128i *)(src + src_stride + 32));
s[7] = _mm_loadu_si128((const __m128i *)(src + src_stride + 48));
// 000 001 100 101 xx xx xx xx 004 005 104 105 xx xx xx xx // 008 009 108 109 xx xx xx xx 00C 00D 10C 10D xx xx xx xx // 010 011 110 111 xx xx xx xx 014 015 114 115 xx xx xx xx // 018 019 118 119 xx xx xx xx 01C 01D 11C 11D xx xx xx xx // 020 021 120 121 xx xx xx xx 024 025 124 125 xx xx xx xx // 028 029 128 129 xx xx xx xx 02C 02D 12C 12D xx xx xx xx // 030 031 130 131 xx xx xx xx 034 035 134 135 xx xx xx xx // 038 039 138 139 xx xx xx xx 03C 03D 13C 13D xx xx xx xx
d[0] = _mm_unpacklo_epi16(s[0], s[4]);
d[1] = _mm_unpackhi_epi16(s[0], s[4]);
d[2] = _mm_unpacklo_epi16(s[1], s[5]);
d[3] = _mm_unpackhi_epi16(s[1], s[5]);
d[4] = _mm_unpacklo_epi16(s[2], s[6]);
d[5] = _mm_unpackhi_epi16(s[2], s[6]);
d[6] = _mm_unpacklo_epi16(s[3], s[7]);
d[7] = _mm_unpackhi_epi16(s[3], s[7]);
// 000 001 100 101 008 009 108 109 xx xx xx xx xx xx xx xx // 004 005 104 105 00C 00D 10C 10D xx xx xx xx xx xx xx xx // 010 011 110 111 018 019 118 119 xx xx xx xx xx xx xx xx // 014 015 114 115 01C 01D 11C 11D xx xx xx xx xx xx xx xx // 020 021 120 121 028 029 128 129 xx xx xx xx xx xx xx xx // 024 025 124 125 02C 02D 12C 12D xx xx xx xx xx xx xx xx // 030 031 130 131 038 039 138 139 xx xx xx xx xx xx xx xx // 034 035 134 135 03C 03D 13C 13D xx xx xx xx xx xx xx xx
s[0] = _mm_unpacklo_epi32(d[0], d[1]);
s[1] = _mm_unpackhi_epi32(d[0], d[1]);
s[2] = _mm_unpacklo_epi32(d[2], d[3]);
s[3] = _mm_unpackhi_epi32(d[2], d[3]);
s[4] = _mm_unpacklo_epi32(d[4], d[5]);
s[5] = _mm_unpackhi_epi32(d[4], d[5]);
s[6] = _mm_unpacklo_epi32(d[6], d[7]);
s[7] = _mm_unpackhi_epi32(d[6], d[7]);
// 00 10 20 30 40 50 60 70 xx xx xx xx xx xx xx xx // 01 11 21 31 41 51 61 71 xx xx xx xx xx xx xx xx
d[0] = _mm_packus_epi16(d[0], d[0]);
d[1] = _mm_packus_epi16(d[1], d[1]); // 00 10 01 11 20 30 21 31 40 50 41 51 60 70 61 71
d[0] = _mm_unpacklo_epi16(d[0], d[1]);
store_8bit_4x4_sse2(d[0], t, 2 * width_hor);
s[0] = s[4];
s[1] = s[5];
t += 4;
x -= 2;
} while (x);
src += 8 * src_stride - 4 * width_hor;
t += 6 * width_hor;
y -= 8;
} while (y);
// Sub 64 to avoid overflow. // Coef 128 would be treated as -128 in PMADDUBSW. Sub 64 here. // Coef 128 is in either fx[1] or fx[2] depending on the phase idx. // When filter phase idx is 1, the two biggest coefficients are shuffled // together, and the sum of them are always no less than 128. Sub 64 here. // After the subtraction, when the sum of all positive coefficients are no // larger than 128, and the sum of all negative coefficients are no // less than -128, there will be no overflow in the convolve8 functions.
f0[1] = _mm_sub_epi8(f0[1], _mm_set1_epi8(64));
f1[1 + offset_idx1] = _mm_sub_epi8(f1[1 + offset_idx1], _mm_set1_epi8(64));
f2[1 + offset_idx2] = _mm_sub_epi8(f2[1 + offset_idx2], _mm_set1_epi8(64));
// Only calculate odd columns since even columns are just src pixels' copies. staticvoid scale_1_to_2_phase_0_row(const uint8_t *src, uint8_t *dst, const int w, const __m128i *const f) {
int x = w;
if (!has_normative_scaler) {
av1_resize_and_extend_frame_c(src, dst, filter, phase, num_planes); return;
}
// We use AOMMIN(num_planes, MAX_MB_PLANE) instead of num_planes to quiet // the static analysis warnings.
int malloc_failed = 0;
for (int i = 0; i < AOMMIN(num_planes, MAX_MB_PLANE); ++i) { const int is_uv = i > 0; const int src_w = src->crop_widths[is_uv]; const int src_h = src->crop_heights[is_uv]; const int src_y_w = (src->crop_widths[0] + 1) & ~1; const int dst_w = dst->crop_widths[is_uv]; const int dst_h = dst->crop_heights[is_uv]; const int dst_y_w = (dst->crop_widths[0] + 1) & ~1; const int dst_y_h = (dst->crop_heights[0] + 1) & ~1;
if (2 * dst_w == src_w && 2 * dst_h == src_h) { // 2 to 1
if (phase == 0) {
scale_plane_2_to_1_phase_0(src->buffers[i], src->strides[is_uv],
dst->buffers[i], dst->strides[is_uv], dst_w,
dst_h);
} else if (filter == BILINEAR) { const int16_t c0 = av1_bilinear_filters[phase][3]; const int16_t c1 = av1_bilinear_filters[phase][4]; const __m128i c0c1 = _mm_set1_epi16(c0 | (c1 << 8)); // c0 and c1 >= 0
scale_plane_2_to_1_bilinear(src->buffers[i], src->strides[is_uv],
dst->buffers[i], dst->strides[is_uv], dst_w,
dst_h, c0c1);
} else { const int buffer_stride = (dst_y_w + 3) & ~3; const int buffer_height = (2 * dst_y_h + SUBPEL_TAPS - 2 + 7) & ~7;
uint8_t *const temp_buffer =
(uint8_t *)malloc(buffer_stride * buffer_height);
if (!temp_buffer) {
malloc_failed = 1; break;
} const InterpKernel *interp_kernel =
(const InterpKernel *)av1_interp_filter_params_list[filter]
.filter_ptr;
scale_plane_2_to_1_general(src->buffers[i], src->strides[is_uv],
dst->buffers[i], dst->strides[is_uv], dst_w,
dst_h, interp_kernel[phase], temp_buffer);
free(temp_buffer);
}
} else if (4 * dst_w == src_w && 4 * dst_h == src_h) { // 4 to 1
if (phase == 0) {
scale_plane_4_to_1_phase_0(src->buffers[i], src->strides[is_uv],
dst->buffers[i], dst->strides[is_uv], dst_w,
dst_h);
} else if (filter == BILINEAR) { const int16_t c0 = av1_bilinear_filters[phase][3]; const int16_t c1 = av1_bilinear_filters[phase][4]; const __m128i c0c1 = _mm_set1_epi16(c0 | (c1 << 8)); // c0 and c1 >= 0
scale_plane_4_to_1_bilinear(src->buffers[i], src->strides[is_uv],
dst->buffers[i], dst->strides[is_uv], dst_w,
dst_h, c0c1);
} else { const int buffer_stride = (dst_y_w + 1) & ~1; const int buffer_height = (4 * dst_y_h + SUBPEL_TAPS - 2 + 7) & ~7; // When dst_w is 1 or 2, we need extra padding to avoid heap read // overflow const int extra_padding = 16;
uint8_t *const temp_buffer =
(uint8_t *)malloc(buffer_stride * buffer_height + extra_padding);
if (!temp_buffer) {
malloc_failed = 1; break;
} const InterpKernel *interp_kernel =
(const InterpKernel *)av1_interp_filter_params_list[filter]
.filter_ptr;
scale_plane_4_to_1_general(src->buffers[i], src->strides[is_uv],
dst->buffers[i], dst->strides[is_uv], dst_w,
dst_h, interp_kernel[phase], temp_buffer);
free(temp_buffer);
}
} else if (4 * dst_w == 3 * src_w && 4 * dst_h == 3 * src_h) { // 4 to 3 const int buffer_stride_hor = (dst_y_w + 5) - ((dst_y_w + 5) % 6) + 2; const int buffer_stride_ver = (dst_y_w + 7) & ~7; const int buffer_height = (4 * dst_y_h / 3 + SUBPEL_TAPS - 1 + 7) & ~7; // When the vertical filter reads more pixels than the horizontal filter // generated in each row, we need extra padding to avoid heap read // overflow. For example, the horizontal filter generates 18 pixels but // the vertical filter reads 24 pixels in a row. The difference is // multiplied by 2 since two rows are interlaced together in the // optimization. const int extra_padding =
(buffer_stride_ver > buffer_stride_hor)
? 2 * (buffer_stride_ver - buffer_stride_hor)
: 0; const int buffer_size = buffer_stride_hor * buffer_height + extra_padding;
uint8_t *const temp_buffer = (uint8_t *)malloc(buffer_size);
if (!temp_buffer) {
malloc_failed = 1; break;
} const InterpKernel *interp_kernel =
(const InterpKernel *)av1_interp_filter_params_list[filter]
.filter_ptr;
scale_plane_4_to_3_general(src->buffers[i], src->strides[is_uv],
dst->buffers[i], dst->strides[is_uv], dst_w,
dst_h, interp_kernel, phase, temp_buffer);
free(temp_buffer);
} else {
assert(dst_w == src_w * 2 && dst_h == src_h * 2); // 1 to 2
uint8_t *const temp_buffer = (uint8_t *)malloc(8 * ((src_y_w + 7) & ~7));
if (!temp_buffer) {
malloc_failed = 1; break;
} const InterpKernel *interp_kernel =
(const InterpKernel *)av1_interp_filter_params_list[filter]
.filter_ptr;
scale_plane_1_to_2_phase_0(src->buffers[i], src->strides[is_uv],
dst->buffers[i], dst->strides[is_uv], src_w,
src_h, interp_kernel[8], temp_buffer);
free(temp_buffer);
}
}
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.