This is an automated email from the git hooks/post-receive script.

git pushed a commit to branch fix-release-build
in repository efl.

View the commit online.

commit e5a045f2b225ec7e57d9ea88e64c6adcf88adff4
Author: [email protected] <[email protected]>
AuthorDate: Mon Aug 3 21:16:42 2026 -0600

    evas: port blend pixel+mask group to AVX2, 6/6 slots bit-exact vs C
    
    op_blend_pixel_mask_sse3.c has no working kernel to port from: both
    span bodies sit behind #if 0 with a "FIXME: probably wrong" comment,
    and every slot in init_blend_pixel_mask_span_funcs_sse3 resolves to
    NULL. So this path runs plain C on x86 today; the binding gate is
    evas_avx2_ops (vs C), not vs SSE3.
    
    Ported op_blend_pixel_mask_avx2.c from the plain-C reference instead.
    The C reference's mask-alpha 0/255 special cases turn out to be
    algebraically identical to its general MUL_SYM/MUL_256 default branch,
    so one unbranched vector kernel (mirroring the disabled SSE3 body's
    mul_sym -> sub4_alpha -> mul_256 -> add shape, using the
    already-validated-bit-exact-vs-C mul_sym_avx2/mul_256_avx2 helpers)
    covers every mask value exactly. The C reference also aliases
    SP/SP_AS/SP_AN and DP/DP_AN straight to one function, so this file has
    one real kernel body behind all six slots.
    
    Mask widening: the 4-wide stage keeps the disabled SSE3 body's
    byte-by-byte _mm_set_epi32 load verbatim. The 8- and 16-wide stages
    use _mm256_cvtepu8_epi32 on a 64-bit mask load, a pure widening
    convert (lane i <- byte i, no cross-lane shuffle), avoiding the
    lane-crossing trap this shape invites.
    
    evas_avx2_ops: 32 c/avx2 pairs (up from 26), 0 outside tolerance,
    PASS. evas_avx2_vs_sse3_ops: 0 diffs, the new slots correctly report
    avx2-only (SSE3 has no working kernel here), PASS.
    Measured with expedite (buffer engine), median of 3 runs per tier at 20
    loops per test, on an -O2 build. AVX2 was selected by leaving
    EVAS_CPU_NO_AVX2 unset; the SSE3 baseline was selected by setting it.
    Note EVAS_CPU_NO_AVX2 is tested by presence, not value - setting it to 0
    still disables AVX2, it must be left unset to enable AVX2.
    
    Tests 3/4/17: geometric mean +29% (test 17, mask-heavy, +75%), pixels
    identical on all three.
    
    Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
---
 .../common/evas_op_blend/op_blend_master_avx2.c    |   2 +
 .../evas_op_blend/op_blend_pixel_mask_avx2.c       | 169 +++++++++++++++++++++
 2 files changed, 171 insertions(+)

diff --git a/src/lib/evas/common/evas_op_blend/op_blend_master_avx2.c b/src/lib/evas/common/evas_op_blend/op_blend_master_avx2.c
index 180817290f..a27368a55d 100644
--- a/src/lib/evas/common/evas_op_blend/op_blend_master_avx2.c
+++ b/src/lib/evas/common/evas_op_blend/op_blend_master_avx2.c
@@ -47,6 +47,7 @@ extern RGBA_Gfx_Func     op_blend_rel_span_funcs[SP_LAST][SM_LAST][SC_LAST][DP_L
 # include "op_blend_pixel_avx2.c"
 # include "op_blend_color_avx2.c"
 # include "op_blend_pixel_color_avx2.c"
+# include "op_blend_pixel_mask_avx2.c"
 
 void
 evas_common_op_blend_init_avx2(void)
@@ -74,6 +75,7 @@ evas_common_op_blend_init_avx2(void)
    init_blend_pixel_span_funcs_avx2();
    init_blend_color_span_funcs_avx2();
    init_blend_pixel_color_span_funcs_avx2();
+   init_blend_pixel_mask_span_funcs_avx2();
 #endif
 }
 
diff --git a/src/lib/evas/common/evas_op_blend/op_blend_pixel_mask_avx2.c b/src/lib/evas/common/evas_op_blend/op_blend_pixel_mask_avx2.c
new file mode 100644
index 0000000000..641cdd8cfb
--- /dev/null
+++ b/src/lib/evas/common/evas_op_blend/op_blend_pixel_mask_avx2.c
@@ -0,0 +1,169 @@
+/* blend pixel x mask --> dst */
+
+/* Ported from the plain-C reference (op_blend_pixel_mask_.c), NOT from
+ * op_blend_pixel_mask_sse3.c. The SSE3 file's two span kernels are compiled
+ * out (#if 0) behind "// FIXME: These functions most likely don't perform
+ * the correct operation. Test them with masks and images." with every one
+ * of the six op-table slots redefined to NULL afterwards - so there is no
+ * working, registered SSE3 kernel for this group to match bit-for-bit, and
+ * the disabled body is explicitly flagged as suspect by whoever wrote it.
+ *
+ * The C reference itself is instructive here: _op_blend_p_mas_dp special
+ * cases mask alpha 0 and 255 for speed, and its *default* case is exactly
+ * MUL_SYM/MUL_256 composition:
+ *
+ *   c = MUL_SYM(alpha, *s); a = 256 - (c >> 24); *d = c + MUL_256(a, *d);
+ *
+ * which is algebraically identical to the special cases too - not merely a
+ * fast path with different results:
+ *   - alpha == 0:   MUL_SYM(0, x) == 0 for every channel (the +0xff00ff
+ *     rounding term never carries past the low byte for a zero multiplier),
+ *     so c == 0, a == 256, and MUL_256(256, d) == d exactly (multiplying by
+ *     256 and shifting right 8 is the identity for an 8-bit channel) -> same
+ *     result as the C special case's "*d unchanged".
+ *   - alpha == 255: MUL_SYM(255, x) == x exactly for every channel (256x -
+ *     x + 255, shifted right 8, is x + floor((255-x)/256) == x since 255-x
+ *     < 256) -> same result as the C special case's "*d = *s + MUL_256(256 -
+ *     (*s>>24), *d)".
+ * So the unbranched general formula below reproduces the C reference for
+ * every mask byte value, not just the ones that fall through its default
+ * case, which is what makes an unconditional vector kernel viable here.
+ *
+ * mul_sym_avx2 and mul_256_avx2 (used below) are bit-exact against the
+ * plain-C MUL_SYM/MUL_256 macros (mul_256_avx2 via the SC_AA "_caa_" kernels
+ * in op_blend_pixel_color_avx2.c; mul_sym_avx2 has the identical
+ * unpack/shuffle(0x88) structure and is bit-exact by the same argument).
+ * Unlike mul4_sym/mul3_sym, they carry no known rounding gap versus their C
+ * macros, so this port targets - and the evas_avx2_ops differential test
+ * confirms - bit-exact-against-C, which is the correct bar here since there
+ * is no SSE3 slot to match instead.
+ *
+ * The C reference aliases _op_blend_pas_mas_dp and _op_blend_pan_mas_dp
+ * straight to _op_blend_p_mas_dp - source alpha state (SP/SP_AS/SP_AN)
+ * does not change the formula, only the mask does. This file follows the
+ * same aliasing, and likewise aliases the DP_AN destination variants
+ * straight to the DP ones, exactly mirroring the C file's alias chain.
+ *
+ * MASK WIDENING: m is DATA8* (1 byte/pixel), s/d are DATA32* (4 bytes/
+ * pixel). The A4OP stage below is the disabled SSE3 body's mask load
+ * verbatim (_mm_set_epi32(m[3],m[2],m[1],m[0])), kept per the file-header
+ * convention used throughout this port (Tasks 8/9) of running the SSE3
+ * A4OP body byte-identical at 4-7 pixels regardless of source. For 8 and 16
+ * pixels this file uses _mm256_cvtepu8_epi32 on a 64-bit/128-bit *load* of
+ * mask bytes, not a shuffle: it is a pure widening convert (lane i of the
+ * result is unpack(byte i of the source), for i in 0..7), so there is no
+ * lane-crossing shuffle pattern to get wrong - byte 0 always lands in
+ * 32-bit lane 0, byte 7 in lane 7, etc, confirmed by the differential test. */
+
+#ifdef BUILD_AVX2
+
+static void
+_op_blend_p_mas_dp_avx2(DATA32 *s, DATA8 *m, DATA32 c EINA_UNUSED, DATA32 *d, int l) {
+
+   int alpha;
+
+   LOOP_ALIGNED_U1_A4_A8_A16(d, l,
+      { /* UOP - byte-identical to the C reference's UOP */
+
+         alpha = *m;
+         switch(alpha)
+           {
+           case 0:
+              break;
+           case 255:
+              alpha = 256 - (*s >> 24);
+              *d = *s + MUL_256(alpha, *d);
+              break;
+           default:
+              c = MUL_SYM(alpha, *s);
+              alpha = 256 - (c >> 24);
+              *d = c + MUL_256(alpha, *d);
+              break;
+           }
+         m++;  s++;  d++; l--;
+      },
+      { /* A4OP - disabled SSE3 A4OP body verbatim, see file header */
+
+         __m128i s0 = _mm_lddqu_si128((__m128i *)s);
+         __m128i d0 = _mm_load_si128((__m128i *)d);
+         __m128i m0 = _mm_set_epi32(m[3], m[2], m[1], m[0]);
+
+         __m128i c0 = mul_sym_sse3(m0, s0);
+         __m128i a0 = sub4_alpha_sse3(c0);
+         __m128i r0 = mul_256_sse3(a0, d0);
+
+         r0 = _mm_add_epi32(r0, c0);
+
+         _mm_store_si128((__m128i *)d, r0);
+
+         m += 4; s += 4; d += 4; l -= 4;
+      },
+      { /* A8OP */
+
+         __m256i s0 = _mm256_loadu_si256((__m256i *)s);
+         __m256i d0 = _mm256_load_si256((__m256i *)d);
+         __m256i m0 = _mm256_cvtepu8_epi32(_mm_loadl_epi64((__m128i *)m));
+
+         __m256i c0 = mul_sym_avx2(m0, s0);
+         __m256i a0 = sub4_alpha_avx2(c0);
+         __m256i r0 = mul_256_avx2(a0, d0);
+
+         r0 = _mm256_add_epi32(r0, c0);
+
+         _mm256_store_si256((__m256i *)d, r0);
+
+         m += 8; s += 8; d += 8; l -= 8;
+      },
+      { /* A16OP */
+
+         __m256i s0 = _mm256_loadu_si256((__m256i *)s);
+         __m256i d0 = _mm256_load_si256((__m256i *)d);
+         __m256i m0 = _mm256_cvtepu8_epi32(_mm_loadl_epi64((__m128i *)m));
+
+         __m256i s1 = _mm256_loadu_si256((__m256i *)(s+8));
+         __m256i d1 = _mm256_load_si256((__m256i *)(d+8));
+         __m256i m1 = _mm256_cvtepu8_epi32(_mm_loadl_epi64((__m128i *)(m+8)));
+
+         __m256i c0 = mul_sym_avx2(m0, s0);
+         __m256i c1 = mul_sym_avx2(m1, s1);
+
+         __m256i a0 = sub4_alpha_avx2(c0);
+         __m256i a1 = sub4_alpha_avx2(c1);
+
+         __m256i r0 = mul_256_avx2(a0, d0);
+         __m256i r1 = mul_256_avx2(a1, d1);
+
+         r0 = _mm256_add_epi32(r0, c0);
+         r1 = _mm256_add_epi32(r1, c1);
+
+         _mm256_store_si256((__m256i *)d, r0);
+         _mm256_store_si256((__m256i *)(d+8), r1);
+
+         m += 16; s += 16; d += 16; l -= 16;
+      })
+}
+
+#define _op_blend_pas_mas_dp_avx2 _op_blend_p_mas_dp_avx2
+#define _op_blend_pan_mas_dp_avx2 _op_blend_pas_mas_dp_avx2
+
+#define _op_blend_p_mas_dpan_avx2 _op_blend_p_mas_dp_avx2
+#define _op_blend_pas_mas_dpan_avx2 _op_blend_pas_mas_dp_avx2
+#define _op_blend_pan_mas_dpan_avx2 _op_blend_pan_mas_dp_avx2
+
+static void
+init_blend_pixel_mask_span_funcs_avx2(void)
+{
+   /* All 6 slots mirroring init_blend_pixel_mask_span_funcs_sse3's slot
+    * list. Bit-exact against C (evas_avx2_ops) - see file header. There is
+    * no working SSE3 kernel to compare against
+    * (evas_avx2_vs_sse3_ops reports these as avx2-only, as expected). */
+   op_blend_span_funcs[SP][SM_AS][SC_N][DP][CPU_AVX2] = _op_blend_p_mas_dp_avx2;
+   op_blend_span_funcs[SP_AS][SM_AS][SC_N][DP][CPU_AVX2] = _op_blend_pas_mas_dp_avx2;
+   op_blend_span_funcs[SP_AN][SM_AS][SC_N][DP][CPU_AVX2] = _op_blend_pan_mas_dp_avx2;
+
+   op_blend_span_funcs[SP][SM_AS][SC_N][DP_AN][CPU_AVX2] = _op_blend_p_mas_dpan_avx2;
+   op_blend_span_funcs[SP_AS][SM_AS][SC_N][DP_AN][CPU_AVX2] = _op_blend_pas_mas_dpan_avx2;
+   op_blend_span_funcs[SP_AN][SM_AS][SC_N][DP_AN][CPU_AVX2] = _op_blend_pan_mas_dpan_avx2;
+}
+
+#endif

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.

Reply via email to