This is an automated email from the git hooks/post-receive script.
git pushed a commit to branch span-gl-clean
in repository efl.
View the commit online.
commit 0b686b9fdbc43ac26b576cbd7ad8f8b233f497b5
Author: [email protected] <[email protected]>
AuthorDate: Mon Aug 10 17:18:40 2026 -0600
fix(evas_ector_gl): evaluate gradients in span-local space
The span shader was rendering every gradient offset from where the
software backend puts it. Expedite's vector tests showed the whole ramp
slid across the shape: on VG Basic Gradient 12.5% of the pixels were more
than 10 dE76 away from the software rendering, far above the ~2.3 dE
just-noticeable threshold.
_span_gradient_linear_coeffs() and _span_gradient_radial_coeffs() both took
offx/offy and both discarded them as EINA_UNUSED. That is wrong: the two
backends evaluate the gradient parameter in different spaces. The software
rasterizer feeds fetch_linear_gradient() the span-local raster coordinates
and applies the ector surface origin to the destination pointer only, so the
origin never reaches the gradient parameter. The shader instead evaluates at
px/py, which are surface-space gl_FragCoord values and do include the origin.
Fold the translation into the constant term so the shader evaluates at
span-local coordinates like the software does. Measured offx/offy of -3 on
the expedite tests, which at a = b = 0.0125 is a ramp offset of 0.075 -
matching the 0.076 offset measured from the rendered pixels.
Linear and radial were both affected, and both are fixed here.
Vector tests against the software backend, worst per-channel delta out of
255, before -> after:
VG Basic Gradient 42 -> 4
VG Basic Radial Gradient 75 -> 5
VG Scaled 41 -> 6
VG Gradient Multi-Shape Transform 26 -> 5
That is now the same noise floor as the non-gradient vector tests, which
sit at 3 to 5. Reproduce with:
expedite-parity -e opengl_x11 -c 20 119 120 125 126
---
src/modules/evas/engines/gl_generic/evas_engine.c | 39 +++++++++++++++++------
1 file changed, 29 insertions(+), 10 deletions(-)
diff --git a/src/modules/evas/engines/gl_generic/evas_engine.c b/src/modules/evas/engines/gl_generic/evas_engine.c
index 08c2412856..404232ba64 100644
--- a/src/modules/evas/engines/gl_generic/evas_engine.c
+++ b/src/modules/evas/engines/gl_generic/evas_engine.c
@@ -2845,7 +2845,7 @@ eng_ector_begin(void *engine, void *surface,
static void
_span_gradient_linear_coeffs(Ector_Renderer_Software_Gradient_Data *gd,
const Eina_Matrix3 *inv,
- int offx EINA_UNUSED, int offy EINA_UNUSED,
+ int offx, int offy,
float atlas_off_x EINA_UNUSED,
float atlas_off_y EINA_UNUSED,
float *out_a, float *out_b, float *out_c)
@@ -2853,21 +2853,34 @@ _span_gradient_linear_coeffs(Ector_Renderer_Software_Gradient_Data *gd,
double dx = gd->linear.dx;
double dy = gd->linear.dy;
double off = gd->linear.off;
+ double a, b;
/* The software computes t = dx*rx + dy*ry + off where:
* rx = inv.xx*(x+0.5) + inv.xy*(y+0.5) + inv.xz
* ry = inv.yx*(x+0.5) + inv.yy*(y+0.5) + inv.yz
*
+ * That (x, y) is the *span-local* raster coordinate. The ector surface
+ * origin (offx, offy) is applied to the destination pointer only
+ * (ector_software_rasterizer.c) and never reaches the gradient
+ * parameter. The shader instead evaluates at px/py in surface space,
+ * which does include that origin, so the translation has to be undone
+ * here — otherwise the gradient slides across the shape by
+ * (a*offx + b*offy) in ramp units.
+ *
* The shader receives px = gl_FragCoord.x - fbo_offset which already
- * equals (x + 0.5) per the OpenGL spec (FragCoord centers at half-integer).
- * So t = a*px + b*py + c decomposes as:
+ * equals (x + 0.5) per the OpenGL spec (FragCoord centers at half-integer),
+ * so span-local (x + 0.5) is px - offx. t = a*px + b*py + c decomposes as:
* a = dx*inv.xx + dy*inv.yx
* b = dx*inv.xy + dy*inv.yy
- * c = dx*inv.xz + dy*inv.yz + off (no extra half-pixel term needed)
+ * c = dx*inv.xz + dy*inv.yz + off - a*offx - b*offy
*/
- *out_a = (float)(dx * inv->xx + dy * inv->yx);
- *out_b = (float)(dx * inv->xy + dy * inv->yy);
- *out_c = (float)(dx * inv->xz + dy * inv->yz + off);
+ a = dx * inv->xx + dy * inv->yx;
+ b = dx * inv->xy + dy * inv->yy;
+
+ *out_a = (float)a;
+ *out_b = (float)b;
+ *out_c = (float)(dx * inv->xz + dy * inv->yz + off
+ - a * (double)offx - b * (double)offy);
}
/**
@@ -2888,7 +2901,7 @@ _span_gradient_linear_coeffs(Ector_Renderer_Software_Gradient_Data *gd,
static void
_span_gradient_radial_coeffs(Ector_Renderer_Software_Gradient_Data *gd,
const Eina_Matrix3 *inv,
- int offx EINA_UNUSED, int offy EINA_UNUSED,
+ int offx, int offy,
float atlas_off_x EINA_UNUSED,
float atlas_off_y EINA_UNUSED,
float *out_a, float *out_b, float *out_c,
@@ -2899,15 +2912,21 @@ _span_gradient_radial_coeffs(Ector_Renderer_Software_Gradient_Data *gd,
* rx = inv.xx*(x+0.5) + inv.xy*(y+0.5) + inv.xz - fx
* ry = inv.yx*(x+0.5) + inv.yy*(y+0.5) + inv.yz - fy
*
+ * As in the linear case that (x, y) is span-local, so the ector surface
+ * origin (offx, offy) must be subtracted from the shader's surface-space
+ * px/py. Fold it into the constant terms.
+ *
* The shader's px/py already include the +0.5 (gl_FragCoord centering),
* so the constant terms are just inv.xz - fx and inv.yz - fy. */
*out_a = (float)inv->xx;
*out_b = (float)inv->xy;
- *out_c = (float)(inv->xz - gd->radial.fx);
+ *out_c = (float)(inv->xz - gd->radial.fx
+ - inv->xx * (double)offx - inv->xy * (double)offy);
*out_d = (float)inv->yx;
*out_e = (float)inv->yy;
- *out_f = (float)(inv->yz - gd->radial.fy);
+ *out_f = (float)(inv->yz - gd->radial.fy
+ - inv->yx * (double)offx - inv->yy * (double)offy);
/* Quadratic parameters — pass inv2a instead of a to avoid
* per-fragment division in the shader. */
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.