diff --git a/docs/2026.html b/docs/2026.html
index 65e3573380..93dafe54f7 100644
--- a/docs/2026.html
+++ b/docs/2026.html
@@ -94,6 +94,7 @@
Improving
SVE2 optimizations of function ReduceGray5x5.
SVE2 optimizations of function SobelDxAbs.
SVE2 optimizations of function SobelDxAbsSum.
+ SVE2 optimizations of function SobelDyAbs.
SVE2 optimizations of function SobelDyAbsSum.
Renaming
diff --git a/src/Simd/SimdSve2Sobel.cpp b/src/Simd/SimdSve2Sobel.cpp
index d60d5921f4..efaf0d685c 100644
--- a/src/Simd/SimdSve2Sobel.cpp
+++ b/src/Simd/SimdSve2Sobel.cpp
@@ -446,11 +446,6 @@ namespace Simd
return (int16_t)Simd::Abs(SobelDy(s0, s2, x0, x1, x2));
}
- SIMD_INLINE int16_t SobelDyAbs(const uint8_t* s0, const uint8_t* s2, size_t x0, size_t x1, size_t x2)
- {
- return (int16_t)Simd::Abs((s2[x0] + 2 * s2[x1] + s2[x2]) - (s0[x0] + 2 * s0[x1] + s0[x2]));
- }
-
template SIMD_INLINE svint16_t SobelDy(const uint8_t* s0, const uint8_t* s2, const svbool_t& mask);
template <> SIMD_INLINE svint16_t SobelDy(const uint8_t* s0, const uint8_t* s2, const svbool_t& mask)
@@ -472,11 +467,6 @@ namespace Simd
return svabs_s16_x(mask, svsub_s16_x(mask, bottom, top));
}
- SIMD_INLINE void SobelDyAbs(const uint8_t* s0, const uint8_t* s2, int16_t* dst, const svbool_t& mask)
- {
- svst1_s16(mask, dst, SobelDyAbs(s0, s2, mask));
- }
-
template SIMD_INLINE void SobelDy(const uint8_t* s0, const uint8_t* s2, int16_t* dst, const svbool_t& mask)
{
svst1_s16(mask, dst, SobelDy(s0, s2, mask));
@@ -514,43 +504,181 @@ namespace Simd
SobelDy(src, srcStride, width, height, (int16_t*)dst, dstStride / sizeof(int16_t));
}
+ SIMD_INLINE svuint16x2_t DyHoriz(const uint8_t* src, const svbool_t& mask)
+ {
+ svuint8_t left = svld1_u8(mask, src - 1);
+ svuint8_t center = svld1_u8(mask, src);
+ svuint8_t right = svld1_u8(mask, src + 1);
+ return svcreate2_u16(
+ svmlalb_n_u16(svaddlb_u16(left, right), center, 2),
+ svmlalt_n_u16(svaddlt_u16(left, right), center, 2));
+ }
+
+ SIMD_INLINE void StoreAbsDy(svuint16x2_t top, svuint16x2_t bot, int16_t* dst, const svbool_t& lo, const svbool_t& hi)
+ {
+ const svbool_t mask16 = svptrue_b16();
+ svint16_t even = svabs_s16_x(mask16, svsub_s16_x(mask16, svreinterpret_s16_u16(svget2(bot, 0)), svreinterpret_s16_u16(svget2(top, 0))));
+ svint16_t odd = svabs_s16_x(mask16, svsub_s16_x(mask16, svreinterpret_s16_u16(svget2(bot, 1)), svreinterpret_s16_u16(svget2(top, 1))));
+ svst1_s16(lo, dst, svzip1_s16(even, odd));
+ svst1_s16(hi, dst + svcnth(), svzip2_s16(even, odd));
+ }
+
+ SIMD_INLINE void SobelDyAbs1(const uint8_t* s0, const uint8_t* s2,
+ int16_t* dst, const svbool_t& mask, const svbool_t& lo, const svbool_t& hi)
+ {
+ StoreAbsDy(DyHoriz(s0, mask), DyHoriz(s2, mask), dst, lo, hi);
+ }
+
+ SIMD_INLINE void SobelDyAbs2(const uint8_t* s0, const uint8_t* s1, const uint8_t* s2, const uint8_t* s3,
+ int16_t* dst0, int16_t* dst1, const svbool_t& mask, const svbool_t& lo, const svbool_t& hi)
+ {
+ svuint16x2_t h0 = DyHoriz(s0, mask);
+ svuint16x2_t h1 = DyHoriz(s1, mask);
+ svuint16x2_t h2 = DyHoriz(s2, mask);
+ svuint16x2_t h3 = DyHoriz(s3, mask);
+ StoreAbsDy(h0, h2, dst0, lo, hi);
+ StoreAbsDy(h1, h3, dst1, lo, hi);
+ }
+
+ SIMD_INLINE void SobelDyAbs4(
+ const uint8_t* s0, const uint8_t* s1, const uint8_t* s2,
+ const uint8_t* s3, const uint8_t* s4, const uint8_t* s5,
+ int16_t* dst0, int16_t* dst1, int16_t* dst2, int16_t* dst3,
+ const svbool_t& mask, const svbool_t& lo, const svbool_t& hi)
+ {
+ svuint16x2_t h0 = DyHoriz(s0, mask);
+ svuint16x2_t h1 = DyHoriz(s1, mask);
+ svuint16x2_t h2 = DyHoriz(s2, mask);
+ svuint16x2_t h3 = DyHoriz(s3, mask);
+ svuint16x2_t h4 = DyHoriz(s4, mask);
+ svuint16x2_t h5 = DyHoriz(s5, mask);
+ StoreAbsDy(h0, h2, dst0, lo, hi);
+ StoreAbsDy(h1, h3, dst1, lo, hi);
+ StoreAbsDy(h2, h4, dst2, lo, hi);
+ StoreAbsDy(h3, h5, dst3, lo, hi);
+ }
+
+ SIMD_INLINE void SobelDyAbsEdge(const uint8_t* s0, const uint8_t* s2, int16_t* dst, size_t width)
+ {
+ dst[0] = SobelDy(s0, s2, 0, 0, 1);
+ dst[width - 1] = SobelDy(s0, s2, width - 2, width - 1, width - 1);
+ }
+
+ void SobelDyAbsBody(const uint8_t* s0, const uint8_t* s2,
+ int16_t* dst, size_t end, size_t A, size_t A2, size_t HA, const svbool_t& all8, const svbool_t& all16)
+ {
+ size_t col = 1;
+ for (; col + A2 <= end; col += A2)
+ {
+ SobelDyAbs1(s0 + col, s2 + col, dst + col, all8, all16, all16);
+ SobelDyAbs1(s0 + col + A, s2 + col + A, dst + col + A, all8, all16, all16);
+ }
+ for (; col + A <= end; col += A)
+ SobelDyAbs1(s0 + col, s2 + col, dst + col, all8, all16, all16);
+ if (col < end)
+ SobelDyAbs1(s0 + col, s2 + col, dst + col, svwhilelt_b8(col, end),
+ svwhilelt_b16(col, end), svwhilelt_b16(col + HA, end));
+ }
+
+ void SobelDyAbsBody2(const uint8_t* s0, const uint8_t* s1, const uint8_t* s2, const uint8_t* s3,
+ int16_t* dst0, int16_t* dst1, size_t end, size_t A, size_t A2, size_t HA, const svbool_t& all8, const svbool_t& all16)
+ {
+ size_t col = 1;
+ for (; col + A2 <= end; col += A2)
+ {
+ SobelDyAbs2(s0 + col, s1 + col, s2 + col, s3 + col, dst0 + col, dst1 + col, all8, all16, all16);
+ SobelDyAbs2(s0 + col + A, s1 + col + A, s2 + col + A, s3 + col + A, dst0 + col + A, dst1 + col + A, all8, all16, all16);
+ }
+ for (; col + A <= end; col += A)
+ SobelDyAbs2(s0 + col, s1 + col, s2 + col, s3 + col, dst0 + col, dst1 + col, all8, all16, all16);
+ if (col < end)
+ SobelDyAbs2(s0 + col, s1 + col, s2 + col, s3 + col, dst0 + col, dst1 + col, svwhilelt_b8(col, end),
+ svwhilelt_b16(col, end), svwhilelt_b16(col + HA, end));
+ }
+
+ void SobelDyAbsBody4(
+ const uint8_t* s0, const uint8_t* s1, const uint8_t* s2,
+ const uint8_t* s3, const uint8_t* s4, const uint8_t* s5,
+ int16_t* dst0, int16_t* dst1, int16_t* dst2, int16_t* dst3,
+ size_t end, size_t A, size_t A2, size_t HA, const svbool_t& all8, const svbool_t& all16)
+ {
+ size_t col = 1;
+ for (; col + A2 <= end; col += A2)
+ {
+ SobelDyAbs4(s0 + col, s1 + col, s2 + col, s3 + col, s4 + col, s5 + col,
+ dst0 + col, dst1 + col, dst2 + col, dst3 + col, all8, all16, all16);
+ SobelDyAbs4(s0 + col + A, s1 + col + A, s2 + col + A, s3 + col + A, s4 + col + A, s5 + col + A,
+ dst0 + col + A, dst1 + col + A, dst2 + col + A, dst3 + col + A, all8, all16, all16);
+ }
+ for (; col + A <= end; col += A)
+ SobelDyAbs4(s0 + col, s1 + col, s2 + col, s3 + col, s4 + col, s5 + col,
+ dst0 + col, dst1 + col, dst2 + col, dst3 + col, all8, all16, all16);
+ if (col < end)
+ SobelDyAbs4(s0 + col, s1 + col, s2 + col, s3 + col, s4 + col, s5 + col,
+ dst0 + col, dst1 + col, dst2 + col, dst3 + col, svwhilelt_b8(col, end),
+ svwhilelt_b16(col, end), svwhilelt_b16(col + HA, end));
+ }
+
void SobelDyAbs(const uint8_t* src, size_t srcStride, size_t width, size_t height, uint8_t* dst, size_t dstStride)
{
assert(dstStride % sizeof(int16_t) == 0);
-
assert(width > 1);
- const size_t A = svcnth();
- const uint8_t* src0, * src1, * src2;
+ const size_t A = svcntb();
+ const size_t A2 = A * 2;
+ const size_t HA = svcnth();
+ const size_t end = width - 1;
+ const svbool_t all8 = svptrue_b8();
+ const svbool_t all16 = svptrue_b16();
int16_t* dst16 = (int16_t*)dst;
size_t dst16Stride = dstStride / sizeof(int16_t);
- for (size_t row = 0; row < height; ++row)
+
+ size_t row = 0;
+ for (; row + 4 <= height; row += 4)
{
- src0 = src + srcStride * (row - 1);
- src1 = src0 + srcStride;
- src2 = src1 + srcStride;
- if (row == 0)
- src0 = src1;
- if (row == height - 1)
- src2 = src1;
+ const uint8_t* src1 = src + srcStride * row;
+ const uint8_t* src0 = row ? src1 - srcStride : src1;
+ const uint8_t* src2 = src1 + srcStride;
+ const uint8_t* src3 = src2 + srcStride;
+ const uint8_t* src4 = src3 + srcStride;
+ const uint8_t* src5 = row + 4 < height ? src4 + srcStride : src4;
+ int16_t* dst0 = dst16 + dst16Stride * row;
+ int16_t* dst1 = dst0 + dst16Stride;
+ int16_t* dst2 = dst1 + dst16Stride;
+ int16_t* dst3 = dst2 + dst16Stride;
- dst16[0] = SobelDyAbs(src0, src2, 0, 0, 1);
- for (size_t col = 1; col < width - 1; col += A)
- SobelDyAbs(src0 + col - 1, src2 + col - 1, dst16 + col, svwhilelt_b16(col, width - 1));
- dst16[width - 1] = SobelDyAbs(src0, src2, width - 2, width - 1, width - 1);
+ SobelDyAbsEdge(src0, src2, dst0, width);
+ SobelDyAbsEdge(src1, src3, dst1, width);
+ SobelDyAbsEdge(src2, src4, dst2, width);
+ SobelDyAbsEdge(src3, src5, dst3, width);
+ SobelDyAbsBody4(src0, src1, src2, src3, src4, src5, dst0, dst1, dst2, dst3, end, A, A2, HA, all8, all16);
+ }
+
+ if (row + 2 <= height)
+ {
+ const uint8_t* src1 = src + srcStride * row;
+ const uint8_t* src0 = row ? src1 - srcStride : src1;
+ const uint8_t* src2 = src1 + srcStride;
+ const uint8_t* src3 = row + 2 < height ? src2 + srcStride : src2;
+ int16_t* dst0 = dst16 + dst16Stride * row;
+ int16_t* dst1 = dst0 + dst16Stride;
- dst16 += dst16Stride;
+ SobelDyAbsEdge(src0, src2, dst0, width);
+ SobelDyAbsEdge(src1, src3, dst1, width);
+ SobelDyAbsBody2(src0, src1, src2, src3, dst0, dst1, end, A, A2, HA, all8, all16);
+ row += 2;
}
- }
- SIMD_INLINE svuint16x2_t DyHoriz(const uint8_t* src, const svbool_t& mask)
- {
- svuint8_t left = svld1_u8(mask, src - 1);
- svuint8_t center = svld1_u8(mask, src);
- svuint8_t right = svld1_u8(mask, src + 1);
- return svcreate2_u16(
- svmlalb_n_u16(svaddlb_u16(left, right), center, 2),
- svmlalt_n_u16(svaddlt_u16(left, right), center, 2));
+ if (row < height)
+ {
+ const uint8_t* src1 = src + srcStride * row;
+ const uint8_t* src0 = row ? src1 - srcStride : src1;
+ const uint8_t* src2 = src1;
+ int16_t* dst0 = dst16 + dst16Stride * row;
+
+ SobelDyAbsEdge(src0, src2, dst0, width);
+ SobelDyAbsBody(src0, src2, dst0, end, A, A2, HA, all8, all16);
+ }
}
SIMD_INLINE void AccumulateAbsDy(svuint16x2_t top, svuint16x2_t bot, svuint32_t& sum)
diff --git a/src/Test/TestFilter.cpp b/src/Test/TestFilter.cpp
index 2b2b388cc3..101e38f02f 100644
--- a/src/Test/TestFilter.cpp
+++ b/src/Test/TestFilter.cpp
@@ -929,11 +929,13 @@ namespace
#ifdef SIMD_SVE2_ENABLE
if (Simd::Sve2::Enable && TestSve2(options))
{
- const int A = (int)svcnth();
+ const int A = (int)svcntb();
result = result && GrayFilterAutoTest(View::Int16, FUNC_G(Simd::Sve2::SobelDyAbs), FUNC_G(SimdSobelDyAbs));
result = result && GrayFilterAutoTest(2, 3, View::Int16, FUNC_G(Simd::Sve2::SobelDyAbs), FUNC_G(SimdSobelDyAbs));
result = result && GrayFilterAutoTest(A + 1, 5, View::Int16, FUNC_G(Simd::Sve2::SobelDyAbs), FUNC_G(SimdSobelDyAbs));
result = result && GrayFilterAutoTest(A + 3, 7, View::Int16, FUNC_G(Simd::Sve2::SobelDyAbs), FUNC_G(SimdSobelDyAbs));
+ result = result && GrayFilterAutoTest(A + 2, 1, View::Int16, FUNC_G(Simd::Sve2::SobelDyAbs), FUNC_G(SimdSobelDyAbs));
+ result = result && GrayFilterAutoTest(A + 2, 4, View::Int16, FUNC_G(Simd::Sve2::SobelDyAbs), FUNC_G(SimdSobelDyAbs));
}
#endif