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 eace6af6bf16fea2e07fee3b9bbbbe21071fd4a8
Author: Cedric BAIL <[email protected]>
AuthorDate: Sun Aug 2 19:38:14 2026 -0600

    evas: fix NEON kernels diverging from the C reference
    
    Several NEON kernels computed something other than the C they stand in
    for. All were found by walking the op tables and comparing every slot
    where both a C and a NEON implementation are registered.
    
    The one that caused visible artifacts is _op_blend_pas_dp_neon(). The C
    reference short circuits a fully transparent source pixel (case 0:
    break). The NEON body selected the untouched destination into the
    accumulator for those lanes but then still added s, yielding s + d
    instead of d. That is identical on premultiplied data, where a zero alpha
    implies a zero pixel, but map and scale interpolation round alpha and
    colour independently and do emit transparent-but-coloured pixels. Select
    after the add rather than before; the instruction count is unchanged.
    
    The rest are transcription slips in kernels that were never exercised:
    
     - _op_copy_rel_p_dp_neon() ignored the source span entirely and
       composited the colour parameter instead, never advancing s;
       _op_copy_rel_pt_p_dp_neon() had the same two operands swapped.
    
     - _op_copy_rel_mas_c_dp_neon() dropped the "*d = 0" for a zero mask.
       This is a copy op, not a blend: a fully masked out pixel must become
       0 rather than be left alone.
    
     - _op_blend_pt_mas_c_dp_neon() was a verbatim copy of the blend_rel
       formula pasted into the plain blend slot, and its cn/can aliases
       pointed at the generic kernel where C uses a specialised one.
    
     - _op_blend_pt_pan_mas_dp_neon() and the pas_can / pas_caa aliases
       pointed at generic kernels instead of the matching specialisations.
    
     - the scalar tail of _op_blend_mas_can_dp_neon() lacked the 0 and 255
       mask cases its own vector body already handles.
    
    Separately, the smooth NEON map loop advanced the per-vertex colour
    interpolator inside its "if (val1 | val2 | val3 | val4)" guard. The C and
    MMX paths advance it unconditionally, once per pixel, so a single fully
    transparent texel quad left the gradient one step behind for the whole
    rest of the span - around 16% of the output pixels wrong in expedite's
    Image Map Color Rotate.
    
    Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
---
 src/lib/evas/common/evas_map_image_loop.c          |  8 +++++-
 .../evas_op_blend/op_blend_mask_color_neon.c       | 28 +++++++++++++++-----
 .../evas_op_blend/op_blend_pixel_color_neon.c      |  4 +--
 .../evas_op_blend/op_blend_pixel_mask_neon.c       |  6 ++++-
 .../common/evas_op_blend/op_blend_pixel_neon.c     | 30 ++++++++++++++++------
 .../common/evas_op_copy/op_copy_mask_color_neon.c  |  1 +
 .../evas/common/evas_op_copy/op_copy_pixel_neon.c  | 10 ++++----
 7 files changed, 64 insertions(+), 23 deletions(-)

diff --git a/src/lib/evas/common/evas_map_image_loop.c b/src/lib/evas/common/evas_map_image_loop.c
index fb8dee33cb..c1b5e99b55 100644
--- a/src/lib/evas/common/evas_map_image_loop.c
+++ b/src/lib/evas/common/evas_map_image_loop.c
@@ -183,7 +183,6 @@
              c2_val3_16x8 = vcombine_u16(c2_16x4, val3_16x4);
 
              cv_16x4 = vdup_n_u16(cv>>16);
-             cv += cd;
              cv_rv_16x8 = vcombine_u16(cv_16x4, rv_16x4);
 
              c2_val3_16x8 = vsubq_u16(c2_val3_16x8, c1_val1_16x8);
@@ -218,6 +217,13 @@
           }
         else
           *d = val1;
+#   if defined(COLMUL) && !defined(COLSAME)
+        /* The colour gradient advances once per pixel. The C and MMX paths do
+         * this unconditionally; doing it inside the branch above skipped it on
+         * fully transparent texels and desynchronised the gradient for the
+         * whole rest of the span. */
+        cv += cd;
+#   endif
 #  else //COLMUL
         val1 = INTERP_256(ru, val2, val1);
         val3 = INTERP_256(ru, val4, val3);
diff --git a/src/lib/evas/common/evas_op_blend/op_blend_mask_color_neon.c b/src/lib/evas/common/evas_op_blend/op_blend_mask_color_neon.c
index 916c2d9d73..2d8c2fd298 100644
--- a/src/lib/evas/common/evas_op_blend/op_blend_mask_color_neon.c
+++ b/src/lib/evas/common/evas_op_blend/op_blend_mask_color_neon.c
@@ -390,8 +390,21 @@ _op_blend_mas_can_dp_neon(DATA32 *s EINA_UNUSED, DATA8 *m, DATA32 c, DATA32 *d,
    end += (size & 3);
    while (start <  end) {
       DATA32 alpha = *m;
-      alpha++;
-      *start = INTERP_256(alpha, c, *start);
+      /* the vector body above selects the untouched dst for alpha == 0
+       * (vbslq_u32) and yields exactly c for alpha == 255; the tail has to
+       * special-case both to stay bit identical to the C reference */
+      switch (alpha)
+        {
+         case 0:
+            break;
+         case 255:
+            *start = c;
+            break;
+         default:
+            alpha++;
+            *start = INTERP_256(alpha, c, *start);
+            break;
+        }
       m++;  start++;
    }
 #else
@@ -650,13 +663,16 @@ init_blend_mask_color_span_funcs_neon(void)
 static void
 _op_blend_pt_mas_c_dp_neon(DATA32 s, DATA8 m, DATA32 c, DATA32 *d) {
    s = MUL_SYM(m, c);
-   c = 256 - (s >> 24);
-   *d = MUL_SYM(*d >> 24, s) + MUL_256(c, *d);
+   m = 255 - (s >> 24);
+   *d = s + MUL_256(m, *d);
 }
 
+static void
+_op_blend_pt_mas_can_dp_neon(DATA32 s EINA_UNUSED, DATA8 m, DATA32 c, DATA32 *d) {
+   *d = INTERP_256(m + 1, c, *d);
+}
 
-#define _op_blend_pt_mas_cn_dp_neon _op_blend_pt_mas_c_dp_neon
-#define _op_blend_pt_mas_can_dp_neon _op_blend_pt_mas_c_dp_neon
+#define _op_blend_pt_mas_cn_dp_neon _op_blend_pt_mas_can_dp_neon
 #define _op_blend_pt_mas_caa_dp_neon _op_blend_pt_mas_c_dp_neon
 
 #define _op_blend_pt_mas_c_dpan_neon _op_blend_pt_mas_c_dp_neon
diff --git a/src/lib/evas/common/evas_op_blend/op_blend_pixel_color_neon.c b/src/lib/evas/common/evas_op_blend/op_blend_pixel_color_neon.c
index d49562ac90..d32df72fd2 100644
--- a/src/lib/evas/common/evas_op_blend/op_blend_pixel_color_neon.c
+++ b/src/lib/evas/common/evas_op_blend/op_blend_pixel_color_neon.c
@@ -711,8 +711,8 @@ _op_blend_pan_caa_dp_neon(DATA32 *s, DATA8 *m EINA_UNUSED, DATA32 c, DATA32 *d,
 }
 
 #define _op_blend_pas_c_dp_neon _op_blend_p_c_dp_neon
-#define _op_blend_pas_can_dp_neon _op_blend_p_c_dp_neon
-#define _op_blend_pas_caa_dp_neon _op_blend_p_c_dp_neon
+#define _op_blend_pas_can_dp_neon _op_blend_p_can_dp_neon
+#define _op_blend_pas_caa_dp_neon _op_blend_p_caa_dp_neon
 
 #define _op_blend_p_c_dpan_neon _op_blend_p_c_dp_neon
 #define _op_blend_pas_c_dpan_neon _op_blend_pas_c_dp_neon
diff --git a/src/lib/evas/common/evas_op_blend/op_blend_pixel_mask_neon.c b/src/lib/evas/common/evas_op_blend/op_blend_pixel_mask_neon.c
index cf22d54345..8405d08751 100644
--- a/src/lib/evas/common/evas_op_blend/op_blend_pixel_mask_neon.c
+++ b/src/lib/evas/common/evas_op_blend/op_blend_pixel_mask_neon.c
@@ -373,7 +373,11 @@ _op_blend_pt_p_mas_dp_neon(DATA32 s, DATA8 m, DATA32 c, DATA32 *d) {
    *d = s + MUL_256(c, *d);
 }
 
-#define _op_blend_pt_pan_mas_dp_neon _op_blend_pt_p_mas_dp_neon
+static void
+_op_blend_pt_pan_mas_dp_neon(DATA32 s, DATA8 m, DATA32 c EINA_UNUSED, DATA32 *d) {
+   *d = INTERP_256(m + 1, s, *d);
+}
+
 #define _op_blend_pt_pas_mas_dp_neon _op_blend_pt_p_mas_dp_neon
 
 #define _op_blend_pt_p_mas_dpan_neon _op_blend_pt_p_mas_dp_neon
diff --git a/src/lib/evas/common/evas_op_blend/op_blend_pixel_neon.c b/src/lib/evas/common/evas_op_blend/op_blend_pixel_neon.c
index 31ad6b21ba..3838fb2074 100644
--- a/src/lib/evas/common/evas_op_blend/op_blend_pixel_neon.c
+++ b/src/lib/evas/common/evas_op_blend/op_blend_pixel_neon.c
@@ -463,14 +463,19 @@ _op_blend_pas_dp_neon(DATA32 *s, DATA8 *m EINA_UNUSED, DATA32 c EINA_UNUSED, DAT
       cond0_32x4 = vceqq_u32(alpha0_32x4, x0_32x4);
       cond1_32x4 = vceqq_u32(alpha1_32x4, x0_32x4);
 
+      ad0_32x4 = vaddq_u32(s0_32x4, ad0_32x4);
+      ad1_32x4 = vaddq_u32(s1_32x4, ad1_32x4);
+
+      /* A source alpha of 0 means "leave the destination alone" in the C
+       * reference (case 0: break). Selecting the untouched destination before
+       * the add still let s through, so a pixel that is transparent but not
+       * colour-zero corrupted dst - and map/scale interpolation produces
+       * exactly those. Select after the add instead; same instruction count. */
       ad0_32x4 = vbslq_u32(cond0_32x4, d0_32x4, ad0_32x4);
       ad1_32x4 = vbslq_u32(cond1_32x4, d1_32x4, ad1_32x4);
 
-      d0_32x4 = vaddq_u32(s0_32x4, ad0_32x4);
-      d1_32x4 = vaddq_u32(s1_32x4, ad1_32x4);
-
-      vst1q_u32(start, d0_32x4);
-      vst1q_u32(start+4, d1_32x4);
+      vst1q_u32(start, ad0_32x4);
+      vst1q_u32(start+4, ad1_32x4);
 
       s+=8;
       start+=8;
@@ -478,9 +483,18 @@ _op_blend_pas_dp_neon(DATA32 *s, DATA8 *m EINA_UNUSED, DATA32 c EINA_UNUSED, DAT
    end += (size & 7);
    while (start <  end)
    {
-      int alpha;
-      alpha = 256 - (*s >> 24);
-      *start = *s++ + MUL_256(alpha, *start);
+      switch (*s & 0xff000000)
+        {
+         case 0:
+            break;
+         case 0xff000000:
+            *start = *s;
+            break;
+         default:
+            *start = *s + MUL_256(256 - (*s >> 24), *start);
+            break;
+        }
+      s++;
       start++;
    }
 #else
diff --git a/src/lib/evas/common/evas_op_copy/op_copy_mask_color_neon.c b/src/lib/evas/common/evas_op_copy/op_copy_mask_color_neon.c
index 854c400d88..ad67d8c536 100644
--- a/src/lib/evas/common/evas_op_copy/op_copy_mask_color_neon.c
+++ b/src/lib/evas/common/evas_op_copy/op_copy_mask_color_neon.c
@@ -98,6 +98,7 @@ _op_copy_rel_mas_c_dp_neon(DATA32 *s EINA_UNUSED, DATA8 *m, DATA32 c, DATA32 *d,
                         switch(color)
                           {
                           case 0:
+                             *d = 0;
                              break;
                           case 255:
                              color = 1 + (*d >> 24);
diff --git a/src/lib/evas/common/evas_op_copy/op_copy_pixel_neon.c b/src/lib/evas/common/evas_op_copy/op_copy_pixel_neon.c
index d2703c19d5..fdf9d2aac3 100644
--- a/src/lib/evas/common/evas_op_copy/op_copy_pixel_neon.c
+++ b/src/lib/evas/common/evas_op_copy/op_copy_pixel_neon.c
@@ -98,13 +98,13 @@ init_copy_pixel_pt_funcs_neon(void)
 
 #ifdef BUILD_NEON
 static void
-_op_copy_rel_p_dp_neon(DATA32 *s EINA_UNUSED, DATA8 *m EINA_UNUSED, DATA32 c EINA_UNUSED, DATA32 *d, int l) {
+_op_copy_rel_p_dp_neon(DATA32 *s, DATA8 *m EINA_UNUSED, DATA32 c EINA_UNUSED, DATA32 *d, int l) {
    // FIXME: neon-it
    DATA32 *e;
    UNROLL8_PLD_WHILE(d, l, e,
                      {
-                        *d = MUL_SYM(*d >> 24, c);
-                        d++;
+                        *d = MUL_SYM(*d >> 24, *s);
+                        d++; s++;
                      });
 }
 
@@ -132,8 +132,8 @@ init_copy_rel_pixel_span_funcs_neon(void)
 #ifdef BUILD_NEON
 static void
 _op_copy_rel_pt_p_dp_neon(DATA32 s, DATA8 m EINA_UNUSED, DATA32 c, DATA32 *d) {
-   s = 1 + (*d >> 24);
-   *d = MUL_256(s, c);
+   c = 1 + (*d >> 24);
+   *d = MUL_256(c, s);
 }
 
 

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

Reply via email to