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

git pushed a commit to reference refs/pull/114/head
in repository efl.

View the commit online.

commit dd06e3449d339c83f54f6ec6b304c8fba45b1ab3
Author: [email protected] <[email protected]>
AuthorDate: Tue May 5 16:21:26 2026 -0600

    refactor(evas_ector_gl): introduce per-variant span vertex structs
    
    The span-buffer GL renderer currently routes all per-shape state through
    uniform uploads — one glUniform call per shape. To enable quad batching,
    per-shape attributes must move into interleaved per-vertex data so that
    multiple shapes can coalesce into a single glDrawArrays call. This commit
    introduces the four interleaved Span_Vertex_* structs (Solid, Solid_Mask,
    Gradient, Gradient_Mask) and a Span_Variant enum that tags which struct
    a pipe entry holds.
    
    ## Per-Variant Struct Design
    
    Each variant encodes its specific set of per-shape parameters into a
    repeatable vertex layout:
    
    - **Span_Vertex_Solid** (24f = 96 B): common header + fill/stroke color
    - **Span_Vertex_Solid_Mask** (30f = 120 B): Solid + mask offset/size + comp method
    - **Span_Vertex_Gradient** (40f = 160 B): common header + gradient coefficients
      (fill abc/ramp_y/def/radial, stroke abc/ramp_y/def/radial)
    - **Span_Vertex_Gradient_Mask** (46f = 184 B): Gradient + mask offset/size + comp method
    
    All variants share a 16f (64 B) common header with NDC position, FBO offset,
    stroke/fill texture flags, x_min thresholds, and premultiplied color.
    
    The mask_comp_inv[2] field in mask variants encodes:
      [0] = raw composite method (cast to float for shader decoding)
      [1] = invert flag (1.0 if MATTE_ALPHA_INVERSE, 0.0 for MATTE_ALPHA)
    
    ## Pipe Entry State
    
    Evas_Engine_GL_Context.pipe[].array gains four new fields:
      - span_vertex_data: heap-grown buffer of interleaved structs
      - span_vertex_data_size: bytes allocated
      - span_vertex_data_used: bytes filled (reset at flush)
      - span_variant: tag indicating which struct variant is in use
    
    High-water mark pattern: size is allocated on first push and reused
    across flush cycles; used is reset at flush. Freed during context cleanup.
    
    ## Dual-Write Transitional Path
    
    evas_gl_common_context_span_push is refactored into two parts:
    
    1. **_span_fill_vertices helper**: takes NDC quad (4 corners, 8 floats) and
       fills 6 vertices (two triangles: TL,TR,BR / TL,BR,BL) of the selected
       variant struct. All per-shape metadata is replicated identically across
       all 6 vertices; pos varies per-vertex from the NDC quad corners.
    
    2. **_span_pipe_find_or_alloc helper**: extracted merge-predicate logic to
       decide whether to reuse the current pipe or allocate a new one. The
       predicate still rejects all merges (conservative for now; Task 4
       will simplify it to ~6 fields and enable batching).
    
    The new span_push path is a dual-write:
      - Canvas-space coords still flow through array.vertex via PUSH_6_VERTICES
        (used by the existing flush path, unchanged)
      - NDC coords + per-shape data also land in span_vertex_data (filled but
        unused at flush-time; Task 4 will flip it to the primary path)
    
    This keeps Task 3 byte-identical on desktop GL: shaders continue reading
    from per-shape uniforms, quads remain unbatched, and both test suites
    (ector: 19/0/0, evas: 120/0/0) pass unchanged.
    
    ## Type Header Extraction
    
    The new evas_ector_gl_span_types.h is a header-only file with:
      - Span_Variant enum and four struct definitions
      - span_vertex_size(variant) helper
      - SPAN_FILL_TYPE_GRADIENT_MIN macro (replaces magic ">= 2" literal)
      - SPAN_PIPE_MAX_QUADS constant (1024 quads max per pipe entry)
    
    Zero dependencies on sw_ft_raster.h, GL headers, or EFL private headers —
    allows sharing between gl_common (which cannot include gl_generic's
    static library headers) and gl_generic. Both now include this single
    canonical source of truth.
    
    ## NDC Conversion in eng_ector_end
    
    The span_push caller (eng_ector_end) pre-converts the canvas-space quad
    to NDC before passing it down. Conversion formula:
      ndc_coord = (canvas_coord / canvas_size) * 2.0 - 1.0
    
    Four corners (TL, TR, BR, BL) are computed in order and passed as
    ndc_quad[8].
    
    Verification:
    - ector suite: 19/0/0
    - evas suite: 120/0/0
    - Build clean
    - Expedite VG benchmark: 10/10 PNGs byte-identical
    
    Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
---
 .../engines/gl_common/evas_ector_gl_span_types.h   | 100 ++++
 .../evas/engines/gl_common/evas_gl_common.h        |  16 +-
 .../evas/engines/gl_common/evas_gl_context.c       | 531 +++++++++++++++------
 .../evas/engines/gl_generic/evas_ector_gl_span.h   |   8 +
 src/modules/evas/engines/gl_generic/evas_engine.c  |  17 +-
 src/modules/evas/engines/gl_generic/meson.build    |   1 +
 6 files changed, 517 insertions(+), 156 deletions(-)

diff --git a/src/modules/evas/engines/gl_common/evas_ector_gl_span_types.h b/src/modules/evas/engines/gl_common/evas_ector_gl_span_types.h
new file mode 100644
index 0000000000..8a8b517b28
--- /dev/null
+++ b/src/modules/evas/engines/gl_common/evas_ector_gl_span_types.h
@@ -0,0 +1,100 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
+/*
+ * Shared type definitions for the span-buffer GL batching path.
+ *
+ * This header is the single source of truth for:
+ *   - Span_Variant enum
+ *   - Span_Vertex_* interleaved vertex structs
+ *   - span_vertex_size() helper
+ *   - SPAN_PIPE_MAX_QUADS and SPAN_FILL_TYPE_GRADIENT_MIN macros
+ *
+ * Deliberately has NO dependency on sw_ft_raster.h, GL headers, or any
+ * EFL private header so it can be included from both gl_common (which
+ * cannot reach gl_generic's static-lib includes) and gl_generic.
+ *
+ * GLfloat is resolved via the GL headers that both callers include before
+ * reaching this header.  In test builds that lack GL headers, define
+ * GLfloat as float before including.
+ */
+
+#ifndef EVAS_ECTOR_GL_SPAN_TYPES_H
+#define EVAS_ECTOR_GL_SPAN_TYPES_H
+
+# ifndef GL_FLOAT
+/* Provide GLfloat for standalone/test include contexts that don't include
+ * full GL headers before this header.  Real GL builds define GL_FLOAT
+ * (and therefore GLfloat) via their GL headers. */
+typedef float GLfloat;
+# endif
+
+/* ---------------------------------------------------------------------------
+ * Fill-type threshold: Span_Data_Type values >= this are gradient types.
+ * Span_Data_Type: 1=Solid, 2=LinearGradient, 3=RadialGradient.
+ * evas_gl_context.c cannot include evas_ector_gl_span.h (sw_ft_raster.h
+ * dependency), so it tests fill.type against this macro instead of the enum.
+ * --------------------------------------------------------------------------- */
+#define SPAN_FILL_TYPE_GRADIENT_MIN 2
+
+/* ---------------------------------------------------------------------------
+ * Span_Variant — selects the interleaved vertex layout for a given draw call.
+ *
+ * SOLID         — no gradient, no mask
+ * SOLID_MASK    — no gradient, composite mask present
+ * GRADIENT      — gradient fill or stroke, no mask
+ * GRADIENT_MASK — gradient fill or stroke, composite mask present
+ * --------------------------------------------------------------------------- */
+typedef enum
+{
+   SPAN_VARIANT_SOLID         = 0,
+   SPAN_VARIANT_SOLID_MASK    = 1,
+   SPAN_VARIANT_GRADIENT      = 2,
+   SPAN_VARIANT_GRADIENT_MASK = 3,
+   SPAN_VARIANT_COUNT
+} Span_Variant;
+
+/* ---------------------------------------------------------------------------
+ * Per-variant interleaved vertex structs for the span attribute-batching path.
+ *
+ * All sizes are in GLfloat units (4 bytes each).  Byte totals are shown in
+ * the trailing comments.  Layout must stay in sync with the vertex shader
+ * attribute offsets documented in:
+ *   docs/superpowers/specs/2026-05-05-span-gl-attribute-batching-design.md
+ *
+ * mask_comp_inv[2]:
+ *   [0] — raw comp_method (Efl_Gfx_Vg_Composite_Method enum value, cast to GLfloat)
+ *   [1] — mask_inv flag (1.0 if the mask alpha should be inverted, 0.0 otherwise)
+ * --------------------------------------------------------------------------- */
+
+/** Common fields shared by all four variants. 16f = 64 B */
+typedef struct { GLfloat pos[2]; GLfloat fbo_fill_off[4]; GLfloat stroke_off_flags[4]; GLfloat x_min[2]; GLfloat mul_col[4]; } Span_Vertex_Common;
+
+/** Solid fill, no mask. 24f = 96 B */
+typedef struct { Span_Vertex_Common c; GLfloat fill_col[4]; GLfloat stroke_col[4]; } Span_Vertex_Solid;
+
+/** Solid fill with composite mask. 30f = 120 B */
+typedef struct { Span_Vertex_Solid s; GLfloat mask_off_size[4]; GLfloat mask_comp_inv[2]; } Span_Vertex_Solid_Mask;
+
+/** Gradient fill or stroke, no mask. 40f = 160 B */
+typedef struct { Span_Vertex_Common c; GLfloat fill_grad_abc_y[4]; GLfloat fill_grad_def[4]; GLfloat fill_grad_radial[4]; GLfloat stroke_grad_abc_y[4]; GLfloat stroke_grad_def[4]; GLfloat stroke_grad_radial[4]; } Span_Vertex_Gradient;
+
+/** Gradient fill or stroke with composite mask. 46f = 184 B */
+typedef struct { Span_Vertex_Gradient g; GLfloat mask_off_size[4]; GLfloat mask_comp_inv[2]; } Span_Vertex_Gradient_Mask;
+
+/** Return the per-vertex byte size for the given variant. */
+static inline size_t
+span_vertex_size(Span_Variant v)
+{
+   switch (v)
+     {
+      case SPAN_VARIANT_SOLID:         return sizeof(Span_Vertex_Solid);
+      case SPAN_VARIANT_SOLID_MASK:    return sizeof(Span_Vertex_Solid_Mask);
+      case SPAN_VARIANT_GRADIENT:      return sizeof(Span_Vertex_Gradient);
+      case SPAN_VARIANT_GRADIENT_MASK: return sizeof(Span_Vertex_Gradient_Mask);
+      default:                         return sizeof(Span_Vertex_Solid);
+     }
+}
+
+/** Maximum number of quads per pipe entry in the span attribute-batching path. */
+#define SPAN_PIPE_MAX_QUADS 1024
+
+#endif /* EVAS_ECTOR_GL_SPAN_TYPES_H */
diff --git a/src/modules/evas/engines/gl_common/evas_gl_common.h b/src/modules/evas/engines/gl_common/evas_gl_common.h
index d499819c6d..4e9ce9a25e 100644
--- a/src/modules/evas/engines/gl_common/evas_gl_common.h
+++ b/src/modules/evas/engines/gl_common/evas_gl_common.h
@@ -46,6 +46,11 @@
 
 #include "evas_gl_define.h"
 
+/* Per-variant span-buffer interleaved vertex types, shared macros, and
+ * the SPAN_FILL_TYPE_GRADIENT_MIN threshold constant.
+ * Single source of truth — no duplication with evas_ector_gl_span.h. */
+#include "evas_ector_gl_span_types.h"
+
 #define EVAS_GL_TILE_SIZE 16
 
 #define SHAD_VERTEX 0
@@ -454,6 +459,14 @@ struct _Evas_Engine_GL_Context
          Eina_Bool      use_mask    : 1;
          Eina_Bool      use_masksam : 1;
          Eina_Bool      anti_alias  : 1;
+         /* Span-buffer attribute batching (Task 3+).
+          * Heap-grown interleaved buffer of Span_Vertex_<variant> structs.
+          * 6 vertices per quad (two triangles).  Filled at push-time;
+          * currently unused at flush-time (Task 4 will flip this). */
+         void          *span_vertex_data;       /* heap-grown; NULL until first push */
+         size_t         span_vertex_data_size;  /* bytes allocated */
+         size_t         span_vertex_data_used;  /* bytes filled */
+         Span_Variant   span_variant;           /* SOLID / SOLID_MASK / GRADIENT / GRADIENT_MASK */
       } array;
    } pipe[MAX_PIPES];
 
@@ -703,7 +716,8 @@ void              evas_gl_common_context_rectangle_push(Evas_Engine_GL_Context *
                                                         Evas_GL_Texture *mtex, int mx, int my, int mw, int mh,
                                                         Eina_Bool mask_smooth, Eina_Bool mask_color);
 void              evas_gl_common_context_span_push(Evas_Engine_GL_Context *gc,
-                                                   const Span_Pipe_Params *p);
+                                                   const Span_Pipe_Params *p,
+                                                   const GLfloat ndc_quad[8]);
 void              evas_gl_common_context_image_push(Evas_Engine_GL_Context *gc,
                                                     Evas_GL_Texture *tex,
                                                     double sx, double sy, double sw, double sh,
diff --git a/src/modules/evas/engines/gl_common/evas_gl_context.c b/src/modules/evas/engines/gl_common/evas_gl_context.c
index 59c6ff39b0..16465f9d71 100644
--- a/src/modules/evas/engines/gl_common/evas_gl_context.c
+++ b/src/modules/evas/engines/gl_common/evas_gl_context.c
@@ -1461,6 +1461,13 @@ evas_gl_common_context_free(Evas_Engine_GL_Context *gc)
              PIPE_FREE(gc->pipe[i].array.mask);
              PIPE_FREE(gc->pipe[i].array.masksam);
              FREE(gc->pipe[i].array.filter_data);
+             if (gc->pipe[i].array.span_vertex_data)
+               {
+                  free(gc->pipe[i].array.span_vertex_data);
+                  gc->pipe[i].array.span_vertex_data      = NULL;
+                  gc->pipe[i].array.span_vertex_data_size = 0;
+                  gc->pipe[i].array.span_vertex_data_used = 0;
+               }
           }
      }
 
@@ -2079,180 +2086,391 @@ evas_gl_common_context_rectangle_push(Evas_Engine_GL_Context *gc,
  * from the standard flush path. */
 static Evas_GL_Program _span_prog_dummy;
 
+/* Fill 6 vertices of the per-variant interleaved struct from Span_Pipe_Params
+ * and pre-computed NDC quad corners.  pos varies per vertex; all other fields
+ * are replicated identically across the 6 vertices of a quad.
+ *
+ * NDC quad order: TL(0), TR(1), BR(2), BL(3) — two triangles: 0,1,2 + 0,2,3.
+ */
+static void
+_span_fill_vertices(void *out_buf, Span_Variant variant,
+                    const Span_Pipe_Params *p,
+                    const GLfloat ndc_quad[8] /* TL,TR,BR,BL: x0y0,x1y0,x1y1,x0y1 */)
+{
+   const int idx[6] = { 0, 1, 2, 0, 2, 3 }; /* triangle fan indices into ndc_quad */
+
+   /* Mask inv: 0=normal, 1=invert, derived from comp_method same as _span_draw_pass */
+   float mask_inv = 0.0f;
+   if (p->comp_method == 2 || p->comp_method == 4) mask_inv = 1.0f;
+
+   /* Build the common header once; pos is overwritten per vertex in the loop. */
+   Span_Vertex_Common common;
+   common.fbo_fill_off[0]     = p->fbo_off_x;
+   common.fbo_fill_off[1]     = p->fbo_off_y;
+   common.fbo_fill_off[2]     = p->fill.off_tx;
+   common.fbo_fill_off[3]     = p->fill.off_ty;
+   common.stroke_off_flags[0] = p->stroke.off_tx;
+   common.stroke_off_flags[1] = p->stroke.off_ty;
+   common.stroke_off_flags[2] = (GLfloat)p->max_spans;
+   common.stroke_off_flags[3] = (GLfloat)((p->fill.tex   ? 1 : 0) |
+                                           (p->stroke.tex ? 2 : 0));
+   common.x_min[0] = (GLfloat)p->fill.x_min;
+   common.x_min[1] = (GLfloat)p->stroke.x_min;
+   /* mul_col: premultiplied ARGB 0xAARRGGBB — decode as R,G,B,A for the shader */
+   common.mul_col[0] = (float)((p->mul_col >> 16) & 0xFF) / 255.0f; /* R */
+   common.mul_col[1] = (float)((p->mul_col >>  8) & 0xFF) / 255.0f; /* G */
+   common.mul_col[2] = (float)( p->mul_col        & 0xFF) / 255.0f; /* B */
+   common.mul_col[3] = (float)((p->mul_col >> 24) & 0xFF) / 255.0f; /* A */
+
+   for (int v = 0; v < 6; v++)
+     {
+        const int corner = idx[v];
+        common.pos[0] = ndc_quad[corner * 2 + 0];
+        common.pos[1] = ndc_quad[corner * 2 + 1];
+
+        switch (variant)
+          {
+           case SPAN_VARIANT_SOLID:
+             {
+                Span_Vertex_Solid *o =
+                   (Span_Vertex_Solid *)((char *)out_buf + v * sizeof(*o));
+                o->c = common;
+                o->fill_col[0]   = (float)((p->fill.col   >> 16) & 0xFF) / 255.0f;
+                o->fill_col[1]   = (float)((p->fill.col   >>  8) & 0xFF) / 255.0f;
+                o->fill_col[2]   = (float)( p->fill.col          & 0xFF) / 255.0f;
+                o->fill_col[3]   = (float)((p->fill.col   >> 24) & 0xFF) / 255.0f;
+                o->stroke_col[0] = (float)((p->stroke.col >> 16) & 0xFF) / 255.0f;
+                o->stroke_col[1] = (float)((p->stroke.col >>  8) & 0xFF) / 255.0f;
+                o->stroke_col[2] = (float)( p->stroke.col        & 0xFF) / 255.0f;
+                o->stroke_col[3] = (float)((p->stroke.col >> 24) & 0xFF) / 255.0f;
+                break;
+             }
+           case SPAN_VARIANT_SOLID_MASK:
+             {
+                Span_Vertex_Solid_Mask *o =
+                   (Span_Vertex_Solid_Mask *)((char *)out_buf + v * sizeof(*o));
+                o->s.c = common;
+                o->s.fill_col[0]   = (float)((p->fill.col   >> 16) & 0xFF) / 255.0f;
+                o->s.fill_col[1]   = (float)((p->fill.col   >>  8) & 0xFF) / 255.0f;
+                o->s.fill_col[2]   = (float)( p->fill.col          & 0xFF) / 255.0f;
+                o->s.fill_col[3]   = (float)((p->fill.col   >> 24) & 0xFF) / 255.0f;
+                o->s.stroke_col[0] = (float)((p->stroke.col >> 16) & 0xFF) / 255.0f;
+                o->s.stroke_col[1] = (float)((p->stroke.col >>  8) & 0xFF) / 255.0f;
+                o->s.stroke_col[2] = (float)( p->stroke.col        & 0xFF) / 255.0f;
+                o->s.stroke_col[3] = (float)((p->stroke.col >> 24) & 0xFF) / 255.0f;
+                o->mask_off_size[0] = p->mask_off_x;
+                o->mask_off_size[1] = p->mask_off_y;
+                o->mask_off_size[2] = p->mask_w;
+                o->mask_off_size[3] = p->mask_h;
+                o->mask_comp_inv[0]   = (GLfloat)p->comp_method;
+                o->mask_comp_inv[1]   = mask_inv;
+                break;
+             }
+           case SPAN_VARIANT_GRADIENT:
+             {
+                Span_Vertex_Gradient *o =
+                   (Span_Vertex_Gradient *)((char *)out_buf + v * sizeof(*o));
+                o->c = common;
+                o->fill_grad_abc_y[0]    = p->fill.grad_a;
+                o->fill_grad_abc_y[1]    = p->fill.grad_b;
+                o->fill_grad_abc_y[2]    = p->fill.grad_c;
+                o->fill_grad_abc_y[3]    = p->fill.grad_ramp_y;
+                o->fill_grad_def[0]      = p->fill.grad_d;
+                o->fill_grad_def[1]      = p->fill.grad_e;
+                o->fill_grad_def[2]      = p->fill.grad_f;
+                o->fill_grad_def[3]      = (GLfloat)p->fill.grad_type;
+                o->fill_grad_radial[0]   = p->fill.grad_ra;
+                o->fill_grad_radial[1]   = p->fill.grad_rdx;
+                o->fill_grad_radial[2]   = p->fill.grad_rdy;
+                o->fill_grad_radial[3]   = (GLfloat)p->fill.grad_spread;
+                o->stroke_grad_abc_y[0]  = p->stroke.grad_a;
+                o->stroke_grad_abc_y[1]  = p->stroke.grad_b;
+                o->stroke_grad_abc_y[2]  = p->stroke.grad_c;
+                o->stroke_grad_abc_y[3]  = p->stroke.grad_ramp_y;
+                o->stroke_grad_def[0]    = p->stroke.grad_d;
+                o->stroke_grad_def[1]    = p->stroke.grad_e;
+                o->stroke_grad_def[2]    = p->stroke.grad_f;
+                o->stroke_grad_def[3]    = (GLfloat)p->stroke.grad_type;
+                o->stroke_grad_radial[0] = p->stroke.grad_ra;
+                o->stroke_grad_radial[1] = p->stroke.grad_rdx;
+                o->stroke_grad_radial[2] = p->stroke.grad_rdy;
+                o->stroke_grad_radial[3] = (GLfloat)p->stroke.grad_spread;
+                break;
+             }
+           case SPAN_VARIANT_GRADIENT_MASK:
+             {
+                Span_Vertex_Gradient_Mask *o =
+                   (Span_Vertex_Gradient_Mask *)((char *)out_buf + v * sizeof(*o));
+                o->g.c = common;
+                o->g.fill_grad_abc_y[0]    = p->fill.grad_a;
+                o->g.fill_grad_abc_y[1]    = p->fill.grad_b;
+                o->g.fill_grad_abc_y[2]    = p->fill.grad_c;
+                o->g.fill_grad_abc_y[3]    = p->fill.grad_ramp_y;
+                o->g.fill_grad_def[0]      = p->fill.grad_d;
+                o->g.fill_grad_def[1]      = p->fill.grad_e;
+                o->g.fill_grad_def[2]      = p->fill.grad_f;
+                o->g.fill_grad_def[3]      = (GLfloat)p->fill.grad_type;
+                o->g.fill_grad_radial[0]   = p->fill.grad_ra;
+                o->g.fill_grad_radial[1]   = p->fill.grad_rdx;
+                o->g.fill_grad_radial[2]   = p->fill.grad_rdy;
+                o->g.fill_grad_radial[3]   = (GLfloat)p->fill.grad_spread;
+                o->g.stroke_grad_abc_y[0]  = p->stroke.grad_a;
+                o->g.stroke_grad_abc_y[1]  = p->stroke.grad_b;
+                o->g.stroke_grad_abc_y[2]  = p->stroke.grad_c;
+                o->g.stroke_grad_abc_y[3]  = p->stroke.grad_ramp_y;
+                o->g.stroke_grad_def[0]    = p->stroke.grad_d;
+                o->g.stroke_grad_def[1]    = p->stroke.grad_e;
+                o->g.stroke_grad_def[2]    = p->stroke.grad_f;
+                o->g.stroke_grad_def[3]    = (GLfloat)p->stroke.grad_type;
+                o->g.stroke_grad_radial[0] = p->stroke.grad_ra;
+                o->g.stroke_grad_radial[1] = p->stroke.grad_rdx;
+                o->g.stroke_grad_radial[2] = p->stroke.grad_rdy;
+                o->g.stroke_grad_radial[3] = (GLfloat)p->stroke.grad_spread;
+                o->mask_off_size[0] = p->mask_off_x;
+                o->mask_off_size[1] = p->mask_off_y;
+                o->mask_off_size[2] = p->mask_w;
+                o->mask_off_size[3] = p->mask_h;
+                o->mask_comp_inv[0]   = (GLfloat)p->comp_method;
+                o->mask_comp_inv[1]   = mask_inv;
+                break;
+             }
+           default: break;
+          }
+     }
+}
+
+/* Find an existing mergeable pipe entry or allocate a new one.
+ * Returns pipe index, or -1 if flush loop was triggered (caller retries). */
+static int
+_span_pipe_find_or_alloc(Evas_Engine_GL_Context *gc,
+                         const Span_Pipe_Params *p,
+                         Span_Variant variant,
+                         float _inv_tw, float _inv_th)
+{
+   int pn = gc->state.top_pipe;
+
+#define _S gc->pipe[pn].shader
+   if (gc->pipe[pn].array.num > 0)
+     {
+        Eina_Bool can_merge = EINA_FALSE;
+
+        if (gc->pipe[pn].region.type == SHD_SPAN &&
+            _S.span_fill_tex          == p->fill.tex        &&
+            _S.span_fill_off_tx       == p->fill.off_tx     &&
+            _S.span_fill_off_ty       == p->fill.off_ty     &&
+            _S.span_fill_col          == p->fill.col        &&
+            _S.span_stroke_tex        == p->stroke.tex      &&
+            _S.span_stroke_off_tx     == p->stroke.off_tx   &&
+            _S.span_stroke_off_ty     == p->stroke.off_ty   &&
+            _S.span_stroke_col        == p->stroke.col      &&
+            _S.span_inv_tw            == _inv_tw            &&
+            _S.span_inv_th            == _inv_th            &&
+            _S.span_max_spans         == p->max_spans       &&
+            _S.span_mul_col           == p->mul_col         &&
+            _S.span_fill_type         == p->fill.type       &&
+            _S.span_stroke_type       == p->stroke.type     &&
+            _S.span_fill_grad_a       == p->fill.grad_a     &&
+            _S.span_fill_grad_b       == p->fill.grad_b     &&
+            _S.span_fill_grad_c       == p->fill.grad_c     &&
+            _S.span_fill_grad_spread  == p->fill.grad_spread &&
+            _S.span_fill_grad_ramp_y  == p->fill.grad_ramp_y  &&
+            _S.span_fill_grad_type    == p->fill.grad_type  &&
+            _S.span_fill_grad_d       == p->fill.grad_d     &&
+            _S.span_fill_grad_e       == p->fill.grad_e     &&
+            _S.span_fill_grad_f       == p->fill.grad_f     &&
+            _S.span_fill_grad_ra      == p->fill.grad_ra    &&
+            _S.span_fill_grad_rdx     == p->fill.grad_rdx   &&
+            _S.span_fill_grad_rdy     == p->fill.grad_rdy   &&
+            _S.span_stroke_grad_a      == p->stroke.grad_a     &&
+            _S.span_stroke_grad_b      == p->stroke.grad_b     &&
+            _S.span_stroke_grad_c      == p->stroke.grad_c     &&
+            _S.span_stroke_grad_spread == p->stroke.grad_spread &&
+            _S.span_stroke_grad_ramp_y == p->stroke.grad_ramp_y  &&
+            _S.span_stroke_grad_type   == p->stroke.grad_type  &&
+            _S.span_stroke_grad_d      == p->stroke.grad_d     &&
+            _S.span_stroke_grad_e      == p->stroke.grad_e     &&
+            _S.span_stroke_grad_f      == p->stroke.grad_f     &&
+            _S.span_stroke_grad_ra     == p->stroke.grad_ra    &&
+            _S.span_stroke_grad_rdx    == p->stroke.grad_rdx   &&
+            _S.span_stroke_grad_rdy    == p->stroke.grad_rdy   &&
+            _S.span_fbo_off_x          == p->fbo_off_x         &&
+            _S.span_fbo_off_y          == p->fbo_off_y         &&
+            _S.span_fill_x_min         == p->fill.x_min        &&
+            _S.span_stroke_x_min       == p->stroke.x_min      &&
+            _S.span_grad_atlas_tex     == p->grad_atlas_tex     &&
+            _S.span_mask_tex           == p->mask_tex           &&
+            _S.span_comp_method        == p->comp_method        &&
+            _S.span_mask_w             == p->mask_w             &&
+            _S.span_mask_h             == p->mask_h             &&
+            _S.span_mask_off_x         == p->mask_off_x         &&
+            _S.span_mask_off_y         == p->mask_off_y)
+          can_merge = EINA_TRUE;
+
+        /* 1024-quad cap: even a matching entry is full if it's at capacity. */
+        if (can_merge)
+          {
+             size_t vsize = span_vertex_size(variant);
+             if (gc->pipe[pn].array.span_vertex_data_used / vsize
+                 >= (size_t)SPAN_PIPE_MAX_QUADS * 6)
+               can_merge = EINA_FALSE; /* full — fall through to new entry */
+          }
+
+        if (!can_merge)
+          {
+             pn = gc->state.top_pipe + 1;
+             if (pn >= gc->shared->info.tune.pipes.max)
+               return -1; /* caller must flush and retry */
+             gc->state.top_pipe = pn;
+          }
+     }
+#undef _S
+   return pn;
+}
+
 void
 evas_gl_common_context_span_push(Evas_Engine_GL_Context *gc,
-                                 const Span_Pipe_Params *p)
+                                 const Span_Pipe_Params *p,
+                                 const GLfloat ndc_quad[8])
 {
-   int pn = 0;
-   /* Precompute reciprocals once; used in both the merge predicate and
-    * the write block below. */
+   /* Precompute reciprocals once for the merge predicate. */
    float _inv_tw = 1.0f / (float)p->pool_w;
    float _inv_th = 1.0f / (float)p->pool_h;
 
-   /* SHD_SPAN pipes carry per-shape uniforms (textures, colors, offsets,
-    * gradient params).  Two pushes may merge ONLY when every uniform
-    * value matches — otherwise the second push's uniforms overwrite the
-    * first's.  Field-by-field comparison with early exit; this also
-    * catches the atlas case where shapes share a GL texture name but
-    * differ in offset/color. */
+   /* Determine variant from fill/stroke gradient types and mask presence.
+    * Span_Data_Type values: 1=Solid, 2=LinearGradient, 3=RadialGradient.
+    * SPAN_FILL_TYPE_GRADIENT_MIN == 2 (defined in evas_ector_gl_span_types.h). */
+   Span_Variant variant;
+   if (p->fill.type  >= SPAN_FILL_TYPE_GRADIENT_MIN ||
+       p->stroke.type >= SPAN_FILL_TYPE_GRADIENT_MIN)
+     variant = (p->mask_tex != 0) ? SPAN_VARIANT_GRADIENT_MASK
+                                   : SPAN_VARIANT_GRADIENT;
+   else
+     variant = (p->mask_tex != 0) ? SPAN_VARIANT_SOLID_MASK
+                                   : SPAN_VARIANT_SOLID;
+
+   int pn;
+ again:
+   pn = _span_pipe_find_or_alloc(gc, p, variant, _inv_tw, _inv_th);
+   if (pn < 0)
+     {
+        shader_array_flush(gc);
+        goto again;
+     }
 
 #define _S gc->pipe[pn].shader
-   /* Float == is intentional: both sides come from the same integer-
-    * derived computation path, so values are bit-identical when equal. */
-   {
-   again:
-      pn = gc->state.top_pipe;
-      if (gc->pipe[pn].array.num > 0)
-        {
-           Eina_Bool can_merge = EINA_FALSE;
-
-           if (gc->pipe[pn].region.type == SHD_SPAN &&
-               _S.span_fill_tex          == p->fill.tex        &&
-               _S.span_fill_off_tx       == p->fill.off_tx     &&
-               _S.span_fill_off_ty       == p->fill.off_ty     &&
-               _S.span_fill_col          == p->fill.col        &&
-               _S.span_stroke_tex        == p->stroke.tex      &&
-               _S.span_stroke_off_tx     == p->stroke.off_tx   &&
-               _S.span_stroke_off_ty     == p->stroke.off_ty   &&
-               _S.span_stroke_col        == p->stroke.col      &&
-               _S.span_inv_tw            == _inv_tw            &&
-               _S.span_inv_th            == _inv_th            &&
-               _S.span_max_spans         == p->max_spans       &&
-               _S.span_mul_col           == p->mul_col         &&
-               _S.span_fill_type         == p->fill.type       &&
-               _S.span_stroke_type       == p->stroke.type     &&
-               _S.span_fill_grad_a       == p->fill.grad_a     &&
-               _S.span_fill_grad_b       == p->fill.grad_b     &&
-               _S.span_fill_grad_c       == p->fill.grad_c     &&
-               _S.span_fill_grad_spread  == p->fill.grad_spread &&
-               _S.span_fill_grad_ramp_y  == p->fill.grad_ramp_y  &&
-               _S.span_fill_grad_type    == p->fill.grad_type  &&
-               _S.span_fill_grad_d       == p->fill.grad_d     &&
-               _S.span_fill_grad_e       == p->fill.grad_e     &&
-               _S.span_fill_grad_f       == p->fill.grad_f     &&
-               _S.span_fill_grad_ra      == p->fill.grad_ra    &&
-               _S.span_fill_grad_rdx     == p->fill.grad_rdx   &&
-               _S.span_fill_grad_rdy     == p->fill.grad_rdy   &&
-               _S.span_stroke_grad_a      == p->stroke.grad_a     &&
-               _S.span_stroke_grad_b      == p->stroke.grad_b     &&
-               _S.span_stroke_grad_c      == p->stroke.grad_c     &&
-               _S.span_stroke_grad_spread == p->stroke.grad_spread &&
-               _S.span_stroke_grad_ramp_y == p->stroke.grad_ramp_y  &&
-               _S.span_stroke_grad_type   == p->stroke.grad_type  &&
-               _S.span_stroke_grad_d      == p->stroke.grad_d     &&
-               _S.span_stroke_grad_e      == p->stroke.grad_e     &&
-               _S.span_stroke_grad_f      == p->stroke.grad_f     &&
-               _S.span_stroke_grad_ra     == p->stroke.grad_ra    &&
-               _S.span_stroke_grad_rdx    == p->stroke.grad_rdx   &&
-               _S.span_stroke_grad_rdy    == p->stroke.grad_rdy   &&
-               _S.span_fbo_off_x          == p->fbo_off_x         &&
-               _S.span_fbo_off_y          == p->fbo_off_y         &&
-               _S.span_fill_x_min         == p->fill.x_min        &&
-               _S.span_stroke_x_min       == p->stroke.x_min      &&
-               _S.span_grad_atlas_tex     == p->grad_atlas_tex     &&
-               _S.span_mask_tex           == p->mask_tex           &&
-               _S.span_comp_method        == p->comp_method        &&
-               _S.span_mask_w             == p->mask_w             &&
-               _S.span_mask_h             == p->mask_h             &&
-               _S.span_mask_off_x         == p->mask_off_x         &&
-               _S.span_mask_off_y         == p->mask_off_y)
-               can_merge = EINA_TRUE;
-
-            if (!can_merge)
-             {
-                pn = gc->state.top_pipe + 1;
-                if (pn >= gc->shared->info.tune.pipes.max)
-                  {
-                     shader_array_flush(gc);
-                     goto again;
-                  }
-                gc->state.top_pipe = pn;
-             }
-        }
-      vertex_array_size_check(gc, pn, 6);
-   }
-
    /* Write span uniform fields — for a merge this is a redundant
     * overwrite with identical values; for a new pipe it initialises. */
-   {
-        gc->pipe[pn].region.type       = SHD_SPAN;
-        gc->pipe[pn].shader.prog       = &_span_prog_dummy;
-        gc->pipe[pn].shader.cur_tex    = p->fill.tex ? p->fill.tex : p->stroke.tex;
-        gc->pipe[pn].shader.blend      = EINA_TRUE;
-        gc->pipe[pn].shader.render_op  = EVAS_RENDER_BLEND;
-        gc->pipe[pn].shader.clip       = 0;
-        gc->pipe[pn].shader.smooth     = 0;
+   gc->pipe[pn].region.type       = SHD_SPAN;
+   gc->pipe[pn].shader.prog       = &_span_prog_dummy;
+   gc->pipe[pn].shader.cur_tex    = p->fill.tex ? p->fill.tex : p->stroke.tex;
+   gc->pipe[pn].shader.blend      = EINA_TRUE;
+   gc->pipe[pn].shader.render_op  = EVAS_RENDER_BLEND;
+   gc->pipe[pn].shader.clip       = 0;
+   gc->pipe[pn].shader.smooth     = 0;
 
-        _S.span_fill_tex      = p->fill.tex;
-        _S.span_fill_off_tx   = p->fill.off_tx;
-        _S.span_fill_off_ty   = p->fill.off_ty;
-        _S.span_fill_col      = p->fill.col;
-        _S.span_stroke_tex    = p->stroke.tex;
-        _S.span_stroke_off_tx = p->stroke.off_tx;
-        _S.span_stroke_off_ty = p->stroke.off_ty;
-        _S.span_stroke_col    = p->stroke.col;
-        _S.span_inv_tw        = _inv_tw;
-        _S.span_inv_th        = _inv_th;
-        _S.span_max_spans     = p->max_spans;
-        _S.span_mul_col       = p->mul_col;
-        _S.span_fill_type     = p->fill.type;
-        _S.span_stroke_type   = p->stroke.type;
+   _S.span_fill_tex      = p->fill.tex;
+   _S.span_fill_off_tx   = p->fill.off_tx;
+   _S.span_fill_off_ty   = p->fill.off_ty;
+   _S.span_fill_col      = p->fill.col;
+   _S.span_stroke_tex    = p->stroke.tex;
+   _S.span_stroke_off_tx = p->stroke.off_tx;
+   _S.span_stroke_off_ty = p->stroke.off_ty;
+   _S.span_stroke_col    = p->stroke.col;
+   _S.span_inv_tw        = _inv_tw;
+   _S.span_inv_th        = _inv_th;
+   _S.span_max_spans     = p->max_spans;
+   _S.span_mul_col       = p->mul_col;
+   _S.span_fill_type     = p->fill.type;
+   _S.span_stroke_type   = p->stroke.type;
 
-        _S.span_fill_grad_a      = p->fill.grad_a;
-        _S.span_fill_grad_b      = p->fill.grad_b;
-        _S.span_fill_grad_c      = p->fill.grad_c;
-        _S.span_fill_grad_spread = p->fill.grad_spread;
-        _S.span_fill_grad_ramp_y = p->fill.grad_ramp_y;
-        _S.span_fill_grad_type   = p->fill.grad_type;
-        _S.span_fill_grad_d      = p->fill.grad_d;
-        _S.span_fill_grad_e      = p->fill.grad_e;
-        _S.span_fill_grad_f      = p->fill.grad_f;
-        _S.span_fill_grad_ra     = p->fill.grad_ra;
-        _S.span_fill_grad_rdx    = p->fill.grad_rdx;
-        _S.span_fill_grad_rdy    = p->fill.grad_rdy;
+   _S.span_fill_grad_a      = p->fill.grad_a;
+   _S.span_fill_grad_b      = p->fill.grad_b;
+   _S.span_fill_grad_c      = p->fill.grad_c;
+   _S.span_fill_grad_spread = p->fill.grad_spread;
+   _S.span_fill_grad_ramp_y = p->fill.grad_ramp_y;
+   _S.span_fill_grad_type   = p->fill.grad_type;
+   _S.span_fill_grad_d      = p->fill.grad_d;
+   _S.span_fill_grad_e      = p->fill.grad_e;
+   _S.span_fill_grad_f      = p->fill.grad_f;
+   _S.span_fill_grad_ra     = p->fill.grad_ra;
+   _S.span_fill_grad_rdx    = p->fill.grad_rdx;
+   _S.span_fill_grad_rdy    = p->fill.grad_rdy;
 
-        _S.span_stroke_grad_a      = p->stroke.grad_a;
-        _S.span_stroke_grad_b      = p->stroke.grad_b;
-        _S.span_stroke_grad_c      = p->stroke.grad_c;
-        _S.span_stroke_grad_spread = p->stroke.grad_spread;
-        _S.span_stroke_grad_ramp_y = p->stroke.grad_ramp_y;
-        _S.span_stroke_grad_type   = p->stroke.grad_type;
-        _S.span_stroke_grad_d      = p->stroke.grad_d;
-        _S.span_stroke_grad_e      = p->stroke.grad_e;
-        _S.span_stroke_grad_f      = p->stroke.grad_f;
-        _S.span_stroke_grad_ra     = p->stroke.grad_ra;
-        _S.span_stroke_grad_rdx    = p->stroke.grad_rdx;
-        _S.span_stroke_grad_rdy    = p->stroke.grad_rdy;
+   _S.span_stroke_grad_a      = p->stroke.grad_a;
+   _S.span_stroke_grad_b      = p->stroke.grad_b;
+   _S.span_stroke_grad_c      = p->stroke.grad_c;
+   _S.span_stroke_grad_spread = p->stroke.grad_spread;
+   _S.span_stroke_grad_ramp_y = p->stroke.grad_ramp_y;
+   _S.span_stroke_grad_type   = p->stroke.grad_type;
+   _S.span_stroke_grad_d      = p->stroke.grad_d;
+   _S.span_stroke_grad_e      = p->stroke.grad_e;
+   _S.span_stroke_grad_f      = p->stroke.grad_f;
+   _S.span_stroke_grad_ra     = p->stroke.grad_ra;
+   _S.span_stroke_grad_rdx    = p->stroke.grad_rdx;
+   _S.span_stroke_grad_rdy    = p->stroke.grad_rdy;
 
-        _S.span_fbo_off_x    = p->fbo_off_x;
-        _S.span_fbo_off_y    = p->fbo_off_y;
-        _S.span_fill_x_min   = p->fill.x_min;
-        _S.span_stroke_x_min = p->stroke.x_min;
+   _S.span_fbo_off_x    = p->fbo_off_x;
+   _S.span_fbo_off_y    = p->fbo_off_y;
+   _S.span_fill_x_min   = p->fill.x_min;
+   _S.span_stroke_x_min = p->stroke.x_min;
 
-        _S.span_grad_atlas_tex = p->grad_atlas_tex;
-        _S.span_mask_tex     = p->mask_tex;
-        _S.span_comp_method  = p->comp_method;
-        _S.span_mask_w       = p->mask_w;
-        _S.span_mask_h       = p->mask_h;
-        _S.span_mask_off_x   = p->mask_off_x;
-        _S.span_mask_off_y   = p->mask_off_y;
-
-        gc->pipe[pn].array.line        = 0;
-        gc->pipe[pn].array.use_vertex  = 1;
-        gc->pipe[pn].array.use_color   = 0;
-        gc->pipe[pn].array.use_texuv   = 0;
-        gc->pipe[pn].array.use_texuv2  = 0;
-        gc->pipe[pn].array.use_texuv3  = 0;
-        gc->pipe[pn].array.use_texa    = 0;
-        gc->pipe[pn].array.use_texsam  = 0;
-        gc->pipe[pn].array.use_mask    = 0;
-        gc->pipe[pn].array.use_masksam = 0;
-     }
+   _S.span_grad_atlas_tex = p->grad_atlas_tex;
+   _S.span_mask_tex     = p->mask_tex;
+   _S.span_comp_method  = p->comp_method;
+   _S.span_mask_w       = p->mask_w;
+   _S.span_mask_h       = p->mask_h;
+   _S.span_mask_off_x   = p->mask_off_x;
+   _S.span_mask_off_y   = p->mask_off_y;
 #undef _S
 
+   gc->pipe[pn].array.line        = 0;
+   gc->pipe[pn].array.use_vertex  = 1;
+   gc->pipe[pn].array.use_color   = 0;
+   gc->pipe[pn].array.use_texuv   = 0;
+   gc->pipe[pn].array.use_texuv2  = 0;
+   gc->pipe[pn].array.use_texuv3  = 0;
+   gc->pipe[pn].array.use_texa    = 0;
+   gc->pipe[pn].array.use_texsam  = 0;
+   gc->pipe[pn].array.use_mask    = 0;
+   gc->pipe[pn].array.use_masksam = 0;
+
    pipe_region_expand(gc, pn, p->x, p->y, p->w, p->h);
+   vertex_array_size_check(gc, pn, 6);
    PIPE_GROW(gc, pn, 6);
    PUSH_6_VERTICES(pn, p->x, p->y, p->w, p->h);
+
+   /* --- Dual-write: also fill span_vertex_data (NDC, Task 3).
+    * The existing array.vertex upload (canvas-space, above) continues to
+    * drive the existing flush.  span_vertex_data carries NDC and is
+    * currently unused at flush-time; Task 4 will flip it to the source
+    * of truth and delete the array.vertex path for span pipes. */
+   {
+      const size_t vsize  = span_vertex_size(variant);
+      const size_t needed = gc->pipe[pn].array.span_vertex_data_used + 6 * vsize;
+      if (gc->pipe[pn].array.span_vertex_data_size < needed)
+        {
+           size_t new_size = gc->pipe[pn].array.span_vertex_data_size;
+           if (new_size == 0) new_size = vsize * 6;
+           while (new_size < needed) new_size *= 2;
+           void *grown = realloc(gc->pipe[pn].array.span_vertex_data, new_size);
+           if (grown)
+             {
+                gc->pipe[pn].array.span_vertex_data      = grown;
+                gc->pipe[pn].array.span_vertex_data_size = new_size;
+             }
+           /* If realloc fails we silently skip the struct fill for this push.
+            * The existing array.vertex path still works, so rendering is
+            * unaffected. */
+        }
+      if (gc->pipe[pn].array.span_vertex_data_size >= needed)
+        {
+           void *write_ptr = (char *)gc->pipe[pn].array.span_vertex_data
+                           + gc->pipe[pn].array.span_vertex_data_used;
+           _span_fill_vertices(write_ptr, variant, p, ndc_quad);
+           gc->pipe[pn].array.span_vertex_data_used += 6 * vsize;
+           gc->pipe[pn].array.span_variant            = variant;
+        }
+   }
 }
 
 #define SWAP(a, b, tmp) \
@@ -4208,6 +4426,10 @@ shader_array_flush(Evas_Engine_GL_Context *gc)
               * subsequent IMAGE push expects them. */
              gc->pipe[i].array.num = 0;
              gc->pipe[i].array.alloc = 0;
+             /* Reset span_vertex_data_used so the buffer is reused
+              * from the start on the next flush cycle (size stays
+              * allocated as a high-water mark). */
+             gc->pipe[i].array.span_vertex_data_used = 0;
              continue;
           }
 
@@ -4906,6 +5128,7 @@ shader_array_flush(Evas_Engine_GL_Context *gc)
 
         gc->pipe[i].array.num = 0;
         gc->pipe[i].array.alloc = 0;
+        gc->pipe[i].array.span_vertex_data_used = 0;
 
         if (glsym_glMapBuffer && glsym_glUnmapBuffer)
           {
diff --git a/src/modules/evas/engines/gl_generic/evas_ector_gl_span.h b/src/modules/evas/engines/gl_generic/evas_ector_gl_span.h
index 5c2e0fba2d..858296c103 100644
--- a/src/modules/evas/engines/gl_generic/evas_ector_gl_span.h
+++ b/src/modules/evas/engines/gl_generic/evas_ector_gl_span.h
@@ -430,4 +430,12 @@ void span_debug_readback(const char *label, unsigned int tex_id,
 #endif /* SPAN_DEBUG_PROBES */
 #endif /* EVAS_GL_COMMON_H */
 
+/* ------------------------------------------------------------------ */
+/* Per-variant interleaved vertex structs (Task 3)                     */
+/* ------------------------------------------------------------------ */
+
+/* Per-variant vertex types, SPAN_PIPE_MAX_QUADS, SPAN_FILL_TYPE_GRADIENT_MIN.
+ * Single source of truth shared with gl_common (no sw_ft_raster.h dependency). */
+#include "../gl_common/evas_ector_gl_span_types.h"
+
 #endif /* EVAS_ECTOR_GL_SPAN_H_ */
diff --git a/src/modules/evas/engines/gl_generic/evas_engine.c b/src/modules/evas/engines/gl_generic/evas_engine.c
index c7352e35ca..a51b03bb81 100644
--- a/src/modules/evas/engines/gl_generic/evas_engine.c
+++ b/src/modules/evas/engines/gl_generic/evas_engine.c
@@ -3312,7 +3312,22 @@ eng_ector_end(void *engine,
                                        }
                                   }
 
-                                evas_gl_common_context_span_push(gc, &_spp);
+                                /* Pre-convert canvas-space quad to NDC for
+                                 * span_vertex_data (Task 3 dual-write).
+                                 * array.vertex still receives canvas-space
+                                 * coords via PUSH_6_VERTICES. */
+                                GLfloat _ndc[8];
+                                float _gw = (float)(gc->w ? gc->w : 1);
+                                float _gh = (float)(gc->h ? gc->h : 1);
+                                float _x0 = (float)_spp.x;
+                                float _y0 = (float)_spp.y;
+                                float _x1 = _x0 + (float)_spp.w;
+                                float _y1 = _y0 + (float)_spp.h;
+                                _ndc[0] = _x0 / _gw * 2.0f - 1.0f; _ndc[1] = _y0 / _gh * 2.0f - 1.0f; /* TL */
+                                _ndc[2] = _x1 / _gw * 2.0f - 1.0f; _ndc[3] = _y0 / _gh * 2.0f - 1.0f; /* TR */
+                                _ndc[4] = _x1 / _gw * 2.0f - 1.0f; _ndc[5] = _y1 / _gh * 2.0f - 1.0f; /* BR */
+                                _ndc[6] = _x0 / _gw * 2.0f - 1.0f; _ndc[7] = _y1 / _gh * 2.0f - 1.0f; /* BL */
+                                evas_gl_common_context_span_push(gc, &_spp, _ndc);
                              }
                           }
                      }
diff --git a/src/modules/evas/engines/gl_generic/meson.build b/src/modules/evas/engines/gl_generic/meson.build
index b969ed3494..bc5ca99bbb 100644
--- a/src/modules/evas/engines/gl_generic/meson.build
+++ b/src/modules/evas/engines/gl_generic/meson.build
@@ -21,6 +21,7 @@ engine_src = files([
 common_engine_src = [
   'evas_gl_private.h',
   'evas_gl_common.h',
+  'evas_ector_gl_span_types.h',
   'evas_gl_define.h',
   'evas_gl_context.c',
   'evas_gl_file_cache.c',

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

Reply via email to