diff --git a/docs/2026.html b/docs/2026.html
index eff69f1b79..c1117c409f 100644
--- a/docs/2026.html
+++ b/docs/2026.html
@@ -94,6 +94,7 @@
Improving
SVE2 optimizations of function ReduceGray5x5.
SVE2 optimizations of class ResizerByteArea1x1.
SVE2 optimizations of class ResizerByteArea2x2.
+ SVE2 optimizations of class ResizerByteBilinear.
SVE2 optimizations of function SobelDx.
SVE2 optimizations of function SobelDxAbs.
SVE2 optimizations of function SobelDxAbsSum.
diff --git a/src/Simd/SimdSve2ResizerBilinear.cpp b/src/Simd/SimdSve2ResizerBilinear.cpp
index 45c8f4f663..0800d40ece 100644
--- a/src/Simd/SimdSve2ResizerBilinear.cpp
+++ b/src/Simd/SimdSve2ResizerBilinear.cpp
@@ -129,14 +129,46 @@ namespace Simd
_bx[1].Resize(size, false, _param.align);
}
- SIMD_INLINE void ResizerByteBilinearInterpolateX(const uint8_t* alpha, uint8_t* buffer)
+ SIMD_INLINE svuint16_t ResizerByteBilinearMaddubs(const svuint8_t& src, const svuint8_t& alpha)
{
- const svbool_t mask8 = svptrue_b8(), mask16 = svptrue_b16();
- svuint8_t _src = svld1_u8(mask8, buffer);
- svuint8_t _alpha = svld1_u8(mask8, alpha);
- svuint16_t lo = svmul_u16_x(mask16, svunpklo_u16(svuzp1_u8(_src, _src)), svunpklo_u16(svuzp1_u8(_alpha, _alpha)));
- lo = svmla_u16_x(mask16, lo, svunpklo_u16(svuzp2_u8(_src, _src)), svunpklo_u16(svuzp2_u8(_alpha, _alpha)));
- svst1_u16(mask16, (uint16_t*)buffer, lo);
+ return svmlalt_u16(svmlalb_u16(svdup_n_u16(0), src, alpha), src, alpha);
+ }
+
+ SIMD_INLINE svuint8_t ResizerByteBilinearShuffleX2()
+ {
+ const svbool_t all = svptrue_b8();
+ svuint8_t i = svindex_u8(0, 1);
+ svuint8_t base = svand_n_u8_x(all, i, 0xFC);
+ svuint8_t even = svlsl_n_u8_x(all, svand_n_u8_x(all, i, 1), 1);
+ svuint8_t odd = svlsr_n_u8_x(all, svand_n_u8_x(all, i, 2), 1);
+ return svadd_u8_x(all, base, svadd_u8_x(all, even, odd));
+ }
+
+ SIMD_INLINE svuint8_t ResizerByteBilinearShuffleX4()
+ {
+ const svbool_t all = svptrue_b8();
+ svuint8_t i = svindex_u8(0, 1);
+ svuint8_t base = svand_n_u8_x(all, i, 0xF8);
+ svuint8_t p = svand_n_u8_x(all, i, 7);
+ svuint8_t lo = svlsl_n_u8_x(all, svand_n_u8_x(all, p, 1), 2);
+ svuint8_t hi = svlsr_n_u8_x(all, p, 1);
+ return svadd_u8_x(all, base, svadd_u8_x(all, lo, hi));
+ }
+
+ template SIMD_INLINE svuint8_t ResizerByteBilinearShuffleX(const svuint8_t& src, const svuint8_t& index)
+ {
+ if (N == 1)
+ return src;
+ else
+ return svtbl_u8(src, index);
+ }
+
+ template SIMD_INLINE void ResizerByteBilinearInterpolateX(const uint8_t* alpha, uint8_t* buffer, const svuint8_t& index)
+ {
+ const svbool_t mask8 = svptrue_b8();
+ const svbool_t mask16 = svptrue_b16();
+ svuint8_t src = ResizerByteBilinearShuffleX(svld1_u8(mask8, buffer), index);
+ svst1_u16(mask16, (uint16_t*)buffer, ResizerByteBilinearMaddubs(src, svld1_u8(mask8, alpha)));
}
SIMD_INLINE svuint8_t PackU16ToU8(const svuint16_t& lo, const svuint16_t& hi)
@@ -144,34 +176,51 @@ namespace Simd
return svuzp1_u8(svqxtnb_u16(lo), svqxtnb_u16(hi));
}
- SIMD_INLINE svuint16_t ResizerByteBilinearInterpolateY(const uint16_t* pbx0, const uint16_t* pbx1,
- const svuint16_t& alpha0, const svuint16_t& alpha1, const svbool_t& mask)
+ SIMD_INLINE void ResizerByteBilinearInterpolateY(const uint16_t* bx0, const uint16_t* bx1,
+ const svuint16_t& alpha0, const svuint16_t& alpha1, uint8_t* dst, const svbool_t& mask)
{
- svuint16_t sum = svmul_u16_x(mask, svld1_u16(mask, pbx0), alpha0);
- sum = svmla_u16_x(mask, sum, svld1_u16(mask, pbx1), alpha1);
- return svlsr_n_u16_x(mask, svadd_n_u16_x(mask, sum, Base::BILINEAR_ROUND_TERM), Base::BILINEAR_SHIFT);
+ svuint16_t sum = svmul_u16_x(mask, svld1_u16(mask, bx0), alpha0);
+ sum = svmla_u16_x(mask, sum, svld1_u16(mask, bx1), alpha1);
+ svst1b_u16(mask, dst, svrshr_n_u16_x(mask, sum, Base::BILINEAR_SHIFT));
}
- SIMD_INLINE void ResizerByteBilinearInterpolateY(const uint8_t* bx0, const uint8_t* bx1,
- const svuint16_t& alpha0, const svuint16_t& alpha1, uint8_t* dst,
- const svbool_t& mask8, const svbool_t& maskLo, const svbool_t& maskHi, size_t half)
+ SIMD_INLINE void ResizerByteBilinearInterpolateYRow(const uint8_t* bx0, const uint8_t* bx1,
+ const svuint16_t& alpha0, const svuint16_t& alpha1, uint8_t* dst, size_t rs)
{
- svuint16_t lo = ResizerByteBilinearInterpolateY((uint16_t*)bx0, (uint16_t*)bx1, alpha0, alpha1, maskLo);
- svuint16_t hi = ResizerByteBilinearInterpolateY((uint16_t*)(bx0 + half * 2), (uint16_t*)(bx1 + half * 2), alpha0, alpha1, maskHi);
- svst1_u8(mask8, dst, PackU16ToU8(lo, hi));
+ const size_t HA = svcnth();
+ const svbool_t mask16 = svptrue_b16();
+ size_t i = 0;
+ for (; i + 2 * HA <= rs; i += 2 * HA)
+ {
+ ResizerByteBilinearInterpolateY((const uint16_t*)(bx0 + 2 * i), (const uint16_t*)(bx1 + 2 * i), alpha0, alpha1, dst + i, mask16);
+ ResizerByteBilinearInterpolateY((const uint16_t*)(bx0 + 2 * (i + HA)), (const uint16_t*)(bx1 + 2 * (i + HA)), alpha0, alpha1, dst + i + HA, mask16);
+ }
+ for (; i + HA <= rs; i += HA)
+ ResizerByteBilinearInterpolateY((const uint16_t*)(bx0 + 2 * i), (const uint16_t*)(bx1 + 2 * i), alpha0, alpha1, dst + i, mask16);
+ if (i < rs)
+ ResizerByteBilinearInterpolateY((const uint16_t*)(bx0 + 2 * i), (const uint16_t*)(bx1 + 2 * i), alpha0, alpha1, dst + i, svwhilelt_b16(i, rs));
}
template void ResizerByteBilinear::Run(const uint8_t* src, size_t srcStride, uint8_t* dst, size_t dstStride)
{
+ struct One { uint8_t val[N]; };
+ struct Two { uint8_t val[N * 2]; };
+
size_t size = 2 * _param.dstW * N;
- const size_t A = svcntb(), HA = svcnth(), DA = 2 * A;
- size_t aligned = AlignHi(size, DA) - DA;
+ size_t rs = _param.dstW * N;
+ const size_t A = svcntb();
ptrdiff_t previous = -2;
uint8_t* bx[2] = { _bx[0].data, _bx[1].data };
const uint8_t* ax = _ax.data;
const int32_t* ix = _ix.data;
size_t dstW = _param.dstW;
+ svuint8_t shuffle = svindex_u8(0, 1);
+ if (N == 2)
+ shuffle = ResizerByteBilinearShuffleX2();
+ else if (N == 4)
+ shuffle = ResizerByteBilinearShuffleX4();
+
for (size_t yDst = 0; yDst < _param.dstH; yDst++, dst += dstStride)
{
svuint16_t a0 = svdup_n_u16((uint16_t)(Base::FRACTION_RANGE - _ay[yDst]));
@@ -192,46 +241,69 @@ namespace Simd
for (; k < 2; k++)
{
- uint8_t* pb = bx[k];
+ uint8_t* pbx = bx[k];
const uint8_t* psrc = src + (sy + k) * srcStride;
- for (size_t x = 0; x < dstW; x++)
+ if (N == 3)
{
- const uint8_t* ps = psrc + ix[x] * N;
- uint8_t* pd = pb + 2 * x * N;
- for (size_t c = 0; c < N; c++)
+ for (size_t x = 0; x < dstW; x++)
{
- pd[2 * c + 0] = ps[c];
- pd[2 * c + 1] = ps[c + N];
+ const uint8_t* ps = psrc + ix[x] * 3;
+ uint8_t* pd = pbx + x * 6;
+ pd[0] = ps[0];
+ pd[1] = ps[3];
+ pd[2] = ps[1];
+ pd[3] = ps[4];
+ pd[4] = ps[2];
+ pd[5] = ps[5];
}
}
+ else
+ {
+ Two* pb = (Two*)pbx;
+ const One* ps = (const One*)psrc;
+ for (size_t x = 0; x < dstW; x++)
+ pb[x] = *(Two*)(ps + ix[x]);
+ }
- for (size_t i = 0; i < size; i += A)
- ResizerByteBilinearInterpolateX(ax + i, pb + i);
+ size_t aligned = AlignLo(size, 2 * A);
+ size_t i = 0;
+ if (N == 3)
+ {
+ for (; i < aligned; i += 2 * A)
+ {
+ ResizerByteBilinearInterpolateX<1>(ax + i, pbx + i, shuffle);
+ ResizerByteBilinearInterpolateX<1>(ax + i + A, pbx + i + A, shuffle);
+ }
+ for (; i < size; i += A)
+ ResizerByteBilinearInterpolateX<1>(ax + i, pbx + i, shuffle);
+ }
+ else
+ {
+ for (; i < aligned; i += 2 * A)
+ {
+ ResizerByteBilinearInterpolateX(ax + i, pbx + i, shuffle);
+ ResizerByteBilinearInterpolateX(ax + i + A, pbx + i + A, shuffle);
+ }
+ for (; i < size; i += A)
+ ResizerByteBilinearInterpolateX(ax + i, pbx + i, shuffle);
+ }
}
- for (size_t ib = 0, id = 0; ib < aligned; ib += DA, id += A)
- ResizerByteBilinearInterpolateY(bx[0] + ib, bx[1] + ib, a0, a1, dst + id, svptrue_b8(), svptrue_b16(), svptrue_b16(), HA);
- size_t i = size - DA;
- ResizerByteBilinearInterpolateY(bx[0] + i, bx[1] + i, a0, a1, dst + i / 2, svwhilelt_b8(i / 2, size / 2),
- svwhilelt_b16(i / 2, size / 2), svwhilelt_b16(i / 2 + HA, size / 2), HA);
+ ResizerByteBilinearInterpolateYRow(bx[0], bx[1], a0, a1, dst, rs);
}
}
template SIMD_INLINE void ResizerByteBilinearLoadGrayInterpolated(const uint8_t* src, const Idx& index, const uint8_t* alpha, uint8_t* dst)
{
const svbool_t mask8 = svptrue_b8();
- svuint8_t _src = svld1_u8(mask8, src + index.src);
- svuint8_t _shuffle = svld1_u8(mask8, index.shuffle);
- svuint8_t _buffer = svtbl_u8(_src, _shuffle);
- svst1_u8(mask8, dst + index.dst, _buffer);
- ResizerByteBilinearInterpolateX(alpha + index.dst, dst + index.dst);
+ const svbool_t mask16 = svptrue_b16();
+ svuint8_t gathered = svtbl_u8(svld1_u8(mask8, src + index.src), svld1_u8(mask8, index.shuffle));
+ svst1_u16(mask16, (uint16_t*)(dst + index.dst), ResizerByteBilinearMaddubs(gathered, svld1_u8(mask8, alpha + index.dst)));
}
void ResizerByteBilinear::RunG(const uint8_t* src, size_t srcStride, uint8_t* dst, size_t dstStride)
{
- size_t size = 2 * _param.dstW;
- const size_t A = svcntb(), HA = svcnth(), DA = 2 * A;
- size_t aligned = AlignHi(size, DA) - DA;
+ size_t rs = _param.dstW;
size_t blocks = _blocks;
ptrdiff_t previous = -2;
uint8_t* bx[2] = { _bx[0].data, _bx[1].data };
@@ -264,11 +336,7 @@ namespace Simd
ResizerByteBilinearLoadGrayInterpolated(psrc, ixg[i], ax, pdst);
}
- for (size_t ib = 0, id = 0; ib < aligned; ib += DA, id += A)
- ResizerByteBilinearInterpolateY(bx[0] + ib, bx[1] + ib, a0, a1, dst + id, svptrue_b8(), svptrue_b16(), svptrue_b16(), HA);
- size_t i = size - DA;
- ResizerByteBilinearInterpolateY(bx[0] + i, bx[1] + i, a0, a1, dst + i / 2, svwhilelt_b8(i / 2, size / 2),
- svwhilelt_b16(i / 2, size / 2), svwhilelt_b16(i / 2 + HA, size / 2), HA);
+ ResizerByteBilinearInterpolateYRow(bx[0], bx[1], a0, a1, dst, rs);
}
}