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.