Merge branch 'wip/smcv/align-gsk-ops' into 'main'

gskpathop: Align graphene_point_t to match gskpathop assumptions where required

Closes #6395

See merge request GNOME/gtk!7510
This commit is contained in:
Benjamin Otte 2024-07-28 17:54:35 +00:00
commit 2e7916cf4f
9 changed files with 177 additions and 98 deletions

View File

@ -532,7 +532,7 @@ struct _GskStandardContour
gsize n_ops;
gsize n_points;
graphene_point_t *points;
GskAlignedPoint *points;
gskpathop ops[];
};
@ -540,19 +540,27 @@ static gsize
gsk_standard_contour_compute_size (gsize n_ops,
gsize n_points)
{
gsize align = MAX (G_ALIGNOF (graphene_point_t),
MAX (G_ALIGNOF (gpointer),
G_ALIGNOF (GskStandardContour)));
gsize s = sizeof (GskStandardContour)
+ sizeof (gskpathop) * n_ops
+ sizeof (graphene_point_t) * n_points;
const gsize point_align = G_ALIGNOF (GskAlignedPoint);
const gsize align = MAX (G_ALIGNOF (GskAlignedPoint),
MAX (G_ALIGNOF (gpointer),
G_ALIGNOF (GskStandardContour)));
gsize s = sizeof (GskStandardContour);
s += sizeof (gskpathop) * n_ops;
/* The array of points needs to be 8-byte aligned, but on 32-bit,
* a single entry in ops might only be 4 bytes, so we might need
* 4 bytes of padding before starting the array of points */
s += (point_align - (s % point_align));
s += sizeof (GskAlignedPoint) * n_points;
return s + (align - (s % align));
}
static void
gsk_standard_contour_init (GskContour *contour,
GskPathFlags flags,
const graphene_point_t *points,
const GskAlignedPoint *points,
gsize n_points,
const gskpathop *ops,
gsize n_ops,
@ -625,8 +633,8 @@ gsk_standard_contour_reverse (const GskContour *contour)
builder = gsk_path_builder_new ();
gsk_path_builder_move_to (builder, self->points[self->n_points - 1].x,
self->points[self->n_points - 1].y);
gsk_path_builder_move_to (builder, self->points[self->n_points - 1].pt.x,
self->points[self->n_points - 1].pt.y);
for (int i = self->n_ops - 1; i >= 0; i--)
gsk_pathop_foreach (self->ops[i], add_reverse, builder);
@ -714,8 +722,8 @@ gsk_standard_contour_get_winding (const GskContour *contour,
GskCurve c;
gsk_curve_init (&c, gsk_pathop_encode (GSK_PATH_CLOSE,
(const graphene_point_t[]) { self->points[self->n_points - 1],
self->points[0] }));
(const GskAlignedPoint[]) { self->points[self->n_points - 1],
self->points[0] }));
winding += gsk_curve_get_crossing (&c, point);
}
@ -748,7 +756,7 @@ gsk_standard_contour_get_closest_point (const GskContour *contour,
{
float dist;
dist = graphene_point_distance (point, &self->points[0], NULL, NULL);
dist = graphene_point_distance (point, &self->points[0].pt, NULL, NULL);
if (dist <= threshold)
{
*out_dist = dist;
@ -799,7 +807,7 @@ gsk_standard_contour_get_position (const GskContour *contour,
if (G_UNLIKELY (point->idx == 0))
{
*position = self->points[0];
*position = self->points[0].pt;
return;
}
@ -1288,36 +1296,43 @@ static const GskContourClass GSK_STANDARD_CONTOUR_CLASS =
static void
gsk_standard_contour_init (GskContour *contour,
GskPathFlags flags,
const graphene_point_t *points,
const GskAlignedPoint *points,
gsize n_points,
const gskpathop *ops,
gsize n_ops,
gssize offset)
{
const gsize align = G_ALIGNOF (GskAlignedPoint);
GskStandardContour *self = (GskStandardContour *) contour;
guint8 *points_addr;
self->contour.klass = &GSK_STANDARD_CONTOUR_CLASS;
self->flags = flags;
self->n_ops = n_ops;
self->n_points = n_points;
self->points = (graphene_point_t *) &self->ops[n_ops];
points_addr = (guint8 *) &self->ops[n_ops];
/* The array of points needs to be 8-byte aligned, but on 32-bit,
* a single entry in ops might only be 4 bytes, so we might need
* 4 bytes of padding before starting the array of points. */
points_addr += align - (((gsize) points_addr) % align);
self->points = (GskAlignedPoint *) points_addr;
memcpy (self->points, points, sizeof (graphene_point_t) * n_points);
offset += self->points - points;
for (gsize i = 0; i < n_ops; i++)
self->ops[i] = gsk_pathop_encode (gsk_pathop_op (ops[i]),
gsk_pathop_points (ops[i]) + offset);
gsk_pathop_aligned_points (ops[i]) + offset);
gsk_bounding_box_init (&self->bounds, &self->points[0], &self->points[0]);
gsk_bounding_box_init (&self->bounds, &self->points[0].pt, &self->points[0].pt);
for (gsize i = 1; i < self->n_points; i ++)
gsk_bounding_box_expand (&self->bounds, &self->points[i]);
gsk_bounding_box_expand (&self->bounds, &self->points[i].pt);
}
GskContour *
gsk_standard_contour_new (GskPathFlags flags,
const graphene_point_t *points,
const GskAlignedPoint *points,
gsize n_points,
const gskpathop *ops,
gsize n_ops,

View File

@ -28,7 +28,7 @@
G_BEGIN_DECLS
GskContour * gsk_standard_contour_new (GskPathFlags flags,
const graphene_point_t *points,
const GskAlignedPoint *points,
gsize n_points,
const gskpathop *ops,
gsize n_ops,

View File

@ -157,30 +157,30 @@ gsk_curve_elevate (const GskCurve *curve,
{
if (curve->op == GSK_PATH_LINE)
{
graphene_point_t p[3];
GskAlignedPoint p[3];
p[0] = curve->line.points[0];
p[0].pt = curve->line.points[0];
graphene_point_interpolate (&curve->line.points[0],
&curve->line.points[1],
0.5,
&p[1]);
p[2] = curve->line.points[1];
&p[1].pt);
p[2].pt = curve->line.points[1];
gsk_curve_init (elevated, gsk_pathop_encode (GSK_PATH_QUAD, p));
}
else if (curve->op == GSK_PATH_QUAD)
{
graphene_point_t p[4];
GskAlignedPoint p[4];
p[0] = curve->quad.points[0];
p[0].pt = curve->quad.points[0];
graphene_point_interpolate (&curve->quad.points[0],
&curve->quad.points[1],
2/3.,
&p[1]);
&p[1].pt);
graphene_point_interpolate (&curve->quad.points[2],
&curve->quad.points[1],
2/3.,
&p[2]);
p[3] = curve->quad.points[2];
&p[2].pt);
p[3].pt = curve->quad.points[2];
gsk_curve_init (elevated, gsk_pathop_encode (GSK_PATH_CUBIC, p));
}
else
@ -300,7 +300,7 @@ gsk_line_curve_pathop (const GskCurve *curve)
{
const GskLineCurve *self = &curve->line;
return gsk_pathop_encode (self->op, self->points);
return gsk_pathop_encode (self->op, self->aligned_points);
}
static const graphene_point_t *
@ -576,7 +576,7 @@ gsk_quad_curve_pathop (const GskCurve *curve)
{
const GskQuadCurve *self = &curve->quad;
return gsk_pathop_encode (self->op, self->points);
return gsk_pathop_encode (self->op, self->aligned_points);
}
static const graphene_point_t *
@ -1003,7 +1003,7 @@ gsk_cubic_curve_pathop (const GskCurve *curve)
{
const GskCubicCurve *self = &curve->cubic;
return gsk_pathop_encode (self->op, self->points);
return gsk_pathop_encode (self->op, self->aligned_points);
}
static const graphene_point_t *
@ -1499,7 +1499,7 @@ gsk_conic_curve_pathop (const GskCurve *curve)
{
const GskConicCurve *self = &curve->conic;
return gsk_pathop_encode (self->op, self->points);
return gsk_pathop_encode (self->op, self->aligned_points);
}
static const graphene_point_t *
@ -1726,7 +1726,7 @@ gsk_conic_curve_split (const GskCurve *curve,
const GskConicCurve *self = &curve->conic;
graphene_point3d_t p[3];
graphene_point3d_t l[3], r[3];
graphene_point_t left[4], right[4];
GskAlignedPoint left[4], right[4];
float w;
/* do de Casteljau in homogeneous coordinates... */
@ -1738,13 +1738,13 @@ gsk_conic_curve_split (const GskCurve *curve,
split_bezier3d (p, 3, progress, l, r);
/* then project the control points down */
left[0] = GRAPHENE_POINT_INIT (l[0].x / l[0].z, l[0].y / l[0].z);
left[1] = GRAPHENE_POINT_INIT (l[1].x / l[1].z, l[1].y / l[1].z);
left[3] = GRAPHENE_POINT_INIT (l[2].x / l[2].z, l[2].y / l[2].z);
left[0].pt = GRAPHENE_POINT_INIT (l[0].x / l[0].z, l[0].y / l[0].z);
left[1].pt = GRAPHENE_POINT_INIT (l[1].x / l[1].z, l[1].y / l[1].z);
left[3].pt = GRAPHENE_POINT_INIT (l[2].x / l[2].z, l[2].y / l[2].z);
right[0] = GRAPHENE_POINT_INIT (r[0].x / r[0].z, r[0].y / r[0].z);
right[1] = GRAPHENE_POINT_INIT (r[1].x / r[1].z, r[1].y / r[1].z);
right[3] = GRAPHENE_POINT_INIT (r[2].x / r[2].z, r[2].y / r[2].z);
right[0].pt = GRAPHENE_POINT_INIT (r[0].x / r[0].z, r[0].y / r[0].z);
right[1].pt = GRAPHENE_POINT_INIT (r[1].x / r[1].z, r[1].y / r[1].z);
right[3].pt = GRAPHENE_POINT_INIT (r[2].x / r[2].z, r[2].y / r[2].z);
/* normalize the outer weights to be 1 by using
* the fact that weights w_i and c*w_i are equivalent
@ -1760,8 +1760,8 @@ gsk_conic_curve_split (const GskCurve *curve,
* the fact that w_0*w_2/w_1^2 is a constant for
* all equivalent weights.
*/
left[2] = GRAPHENE_POINT_INIT (l[1].z / sqrt (l[2].z), 0);
right[2] = GRAPHENE_POINT_INIT (r[1].z / sqrt (r[0].z), 0);
left[2].pt = GRAPHENE_POINT_INIT (l[1].z / sqrt (l[2].z), 0);
right[2].pt = GRAPHENE_POINT_INIT (r[1].z / sqrt (r[0].z), 0);
if (start)
gsk_curve_init (start, gsk_pathop_encode (GSK_PATH_CONIC, left));
@ -1899,17 +1899,17 @@ cubic_approximation (const GskCurve *curve,
GskCurve *cubic)
{
const GskConicCurve *self = &curve->conic;
graphene_point_t p[4];
GskAlignedPoint p[4];
float w = self->points[2].x;
float w2 = w*w;
float lambda;
lambda = 2 * (6*w2 + 1 - sqrt (3*w2 + 1)) / (12*w2 + 3);
p[0] = self->points[0];
p[3] = self->points[3];
graphene_point_interpolate (&self->points[0], &self->points[1], lambda, &p[1]);
graphene_point_interpolate (&self->points[3], &self->points[1], lambda, &p[2]);
p[0].pt = self->points[0];
p[3].pt = self->points[3];
graphene_point_interpolate (&self->points[0], &self->points[1], lambda, &p[1].pt);
graphene_point_interpolate (&self->points[3], &self->points[1], lambda, &p[2].pt);
gsk_curve_init (cubic, gsk_pathop_encode (GSK_PATH_CUBIC, p));
}

View File

@ -41,7 +41,10 @@ struct _GskLineCurve
gboolean padding;
graphene_point_t points[2];
union {
graphene_point_t points[2];
GskAlignedPoint aligned_points[2];
};
};
struct _GskQuadCurve
@ -50,7 +53,10 @@ struct _GskQuadCurve
gboolean has_coefficients;
graphene_point_t points[3];
union {
graphene_point_t points[3];
GskAlignedPoint aligned_points[3];
};
graphene_point_t coeffs[3];
};
@ -61,7 +67,10 @@ struct _GskCubicCurve
gboolean has_coefficients;
graphene_point_t points[4];
union {
graphene_point_t points[4];
GskAlignedPoint aligned_points[4];
};
graphene_point_t coeffs[4];
};
@ -75,7 +84,10 @@ struct _GskConicCurve
/* points[0], points[1], points[3] are the control points,
* points[2].x is the weight
*/
graphene_point_t points[4];
union {
graphene_point_t points[4];
GskAlignedPoint aligned_points[4];
};
graphene_point_t num[3];
graphene_point_t denom[3];

View File

@ -701,6 +701,13 @@ gsk_path_foreach_trampoline (GskPathOperation op,
gpointer data)
{
GskPathForeachTrampoline *trampoline = data;
GskAlignedPoint *aligned = g_alloca (sizeof (graphene_point_t) * n_pts);
/* We can't necessarily guarantee that pts is 8-byte aligned
* (probably it is, but we've been through too many layers of
* indirection to be sure) so copy it into a buffer that is
* definitely suitably-aligned. */
memcpy (aligned, pts, sizeof (graphene_point_t) * n_pts);
switch (op)
{
@ -731,7 +738,7 @@ gsk_path_foreach_trampoline (GskPathOperation op,
trampoline->user_data);
}
gsk_curve_init (&curve, gsk_pathop_encode (GSK_PATH_QUAD, pts));
gsk_curve_init (&curve, gsk_pathop_encode (GSK_PATH_QUAD, aligned));
return gsk_curve_decompose (&curve,
trampoline->tolerance,
gsk_path_foreach_trampoline_add_line,
@ -745,7 +752,7 @@ gsk_path_foreach_trampoline (GskPathOperation op,
if (trampoline->flags & GSK_PATH_FOREACH_ALLOW_CUBIC)
return trampoline->func (op, pts, n_pts, weight, trampoline->user_data);
gsk_curve_init (&curve, gsk_pathop_encode (GSK_PATH_CUBIC, pts));
gsk_curve_init (&curve, gsk_pathop_encode (GSK_PATH_CUBIC, aligned));
if (trampoline->flags & (GSK_PATH_FOREACH_ALLOW_QUAD|GSK_PATH_FOREACH_ALLOW_CONIC))
return gsk_curve_decompose_curve (&curve,
trampoline->flags,
@ -766,7 +773,7 @@ gsk_path_foreach_trampoline (GskPathOperation op,
if (trampoline->flags & GSK_PATH_FOREACH_ALLOW_CONIC)
return trampoline->func (op, pts, n_pts, weight, trampoline->user_data);
gsk_curve_init (&curve, gsk_pathop_encode (GSK_PATH_CONIC, (graphene_point_t[4]) { pts[0], pts[1], { weight, 0.f }, pts[2] } ));
gsk_curve_init (&curve, gsk_pathop_encode (GSK_PATH_CONIC, (GskAlignedPoint[4]) { { pts[0] }, { pts[1] }, { { weight, 0.f } }, { pts[2] } } ));
if (trampoline->flags & (GSK_PATH_FOREACH_ALLOW_QUAD|GSK_PATH_FOREACH_ALLOW_CUBIC))
return gsk_curve_decompose_curve (&curve,
trampoline->flags,

View File

@ -156,7 +156,7 @@ static inline gskpathop
gsk_pathop_encode_index (GskPathOperation op,
gsize index)
{
return gsk_pathop_encode (op, ((graphene_point_t *) NULL) + index);
return gsk_pathop_encode (op, ((GskAlignedPoint *) NULL) + index);
}
static void
@ -193,7 +193,7 @@ gsk_path_builder_end_current (GskPathBuilder *self)
return;
contour = gsk_standard_contour_new (self->flags,
(graphene_point_t *) self->points->data,
(GskAlignedPoint *) self->points->data,
self->points->len,
(gskpathop *) self->ops->data,
self->ops->len,

View File

@ -25,11 +25,52 @@
G_BEGIN_DECLS
/* We assume that arrays of graphene_point_t are aligned on an 8-byte
* boundary, which means we can use the lowest 3 bits to represent up
* to 8 distinct path operations. */
#define GSK_PATHOP_OPERATION_MASK (0x7)
/* graphene_point_t is a struct containing two floats, so an array of
* graphene_point_t on the stack is not necessarily 8-byte aligned
* unless we force it to be.
*
* Using a union for this means the compiler will warn or error if we
* have not handled these correctly: for example we can go from a
* GskAlignedPoint * to a graphene_point_t * (which is always OK) with:
*
* GskAlignedPoint *gap = ...;
* graphene_point_t *gpt;
* gpt = &gap[0].pt;
*
* but going back the other way is not possible without a cast or a
* compiler warning. */
typedef union
{
graphene_point_t pt;
/* On many platforms this will be enough to force the correct alignment. */
guint64 alignment;
/* Unfortunately not all platforms require guint64 to be naturally-aligned
* (for example on i386, only 4-byte alignment is required) so we have to
* try harder. */
#ifdef __GNUC__
__attribute__((aligned(8))) guint64 really_aligned;
#elif defined(_MSC_VER)
__declspec(align(8)) guint64 really_aligned;
#endif
} GskAlignedPoint;
G_STATIC_ASSERT (sizeof (GskAlignedPoint) == sizeof (graphene_point_t));
G_STATIC_ASSERT (G_ALIGNOF (GskAlignedPoint) >= GSK_PATHOP_OPERATION_MASK + 1);
typedef gpointer gskpathop;
static inline
gskpathop gsk_pathop_encode (GskPathOperation op,
const graphene_point_t *pts);
const GskAlignedPoint *pts);
static inline
const GskAlignedPoint *gsk_pathop_aligned_points (gskpathop pop);
static inline
const graphene_point_t *gsk_pathop_points (gskpathop pop);
static inline
@ -57,11 +98,9 @@ void gsk_path_builder_pathop_reverse_to (GskPathBuilder
* operations overlapping, so we can't put the weight at the end.
*/
#define GSK_PATHOP_OPERATION_MASK (0x7)
static inline gskpathop
gsk_pathop_encode (GskPathOperation op,
const graphene_point_t *pts)
const GskAlignedPoint *pts)
{
/* g_assert (op & GSK_PATHOP_OPERATION_MASK == op); */
g_assert ((GPOINTER_TO_SIZE (pts) & GSK_PATHOP_OPERATION_MASK) == 0);
@ -69,10 +108,16 @@ gsk_pathop_encode (GskPathOperation op,
return GSIZE_TO_POINTER (GPOINTER_TO_SIZE (pts) | op);
}
static inline const GskAlignedPoint *
gsk_pathop_aligned_points (gskpathop pop)
{
return GSIZE_TO_POINTER (GPOINTER_TO_SIZE (pop) & ~GSK_PATHOP_OPERATION_MASK);
}
static inline const graphene_point_t *
gsk_pathop_points (gskpathop pop)
{
return GSIZE_TO_POINTER (GPOINTER_TO_SIZE (pop) & ~GSK_PATHOP_OPERATION_MASK);
return &(gsk_pathop_aligned_points (pop)->pt);
}
static inline

View File

@ -25,11 +25,11 @@ static void
test_curve_tangents (void)
{
GskCurve c;
graphene_point_t p[4];
GskAlignedPoint p[4];
graphene_vec2_t t;
graphene_point_init (&p[0], 0, 0);
graphene_point_init (&p[1], 100, 0);
graphene_point_init (&p[0].pt, 0, 0);
graphene_point_init (&p[1].pt, 100, 0);
gsk_curve_init (&c, gsk_pathop_encode (GSK_PATH_LINE, p));
gsk_curve_get_start_tangent (&c, &t);
@ -37,8 +37,8 @@ test_curve_tangents (void)
gsk_curve_get_end_tangent (&c, &t);
g_assert_true (graphene_vec2_near (&t, graphene_vec2_x_axis (), 0.0001));
graphene_point_init (&p[0], 0, 0);
graphene_point_init (&p[1], 0, 100);
graphene_point_init (&p[0].pt, 0, 0);
graphene_point_init (&p[1].pt, 0, 100);
gsk_curve_init (&c, gsk_pathop_encode (GSK_PATH_LINE, p));
gsk_curve_get_start_tangent (&c, &t);
@ -46,10 +46,10 @@ test_curve_tangents (void)
gsk_curve_get_end_tangent (&c, &t);
g_assert_true (graphene_vec2_near (&t, graphene_vec2_y_axis (), 0.0001));
graphene_point_init (&p[0], 0, 0);
graphene_point_init (&p[1], 50, 0);
graphene_point_init (&p[2], 100, 50);
graphene_point_init (&p[3], 100, 100);
graphene_point_init (&p[0].pt, 0, 0);
graphene_point_init (&p[1].pt, 50, 0);
graphene_point_init (&p[2].pt, 100, 50);
graphene_point_init (&p[3].pt, 100, 100);
gsk_curve_init (&c, gsk_pathop_encode (GSK_PATH_CUBIC, p));
gsk_curve_get_start_tangent (&c, &t);
@ -62,13 +62,13 @@ static void
test_curve_degenerate_tangents (void)
{
GskCurve c;
graphene_point_t p[4];
GskAlignedPoint p[4];
graphene_vec2_t t;
graphene_point_init (&p[0], 0, 0);
graphene_point_init (&p[1], 0, 0);
graphene_point_init (&p[2], 100, 0);
graphene_point_init (&p[3], 100, 0);
graphene_point_init (&p[0].pt, 0, 0);
graphene_point_init (&p[1].pt, 0, 0);
graphene_point_init (&p[2].pt, 100, 0);
graphene_point_init (&p[3].pt, 100, 0);
gsk_curve_init (&c, gsk_pathop_encode (GSK_PATH_CUBIC, p));
gsk_curve_get_start_tangent (&c, &t);
@ -76,10 +76,10 @@ test_curve_degenerate_tangents (void)
gsk_curve_get_end_tangent (&c, &t);
g_assert_true (graphene_vec2_near (&t, graphene_vec2_x_axis (), 0.0001));
graphene_point_init (&p[0], 0, 0);
graphene_point_init (&p[1], 50, 0);
graphene_point_init (&p[2], 50, 0);
graphene_point_init (&p[3], 100, 0);
graphene_point_init (&p[0].pt, 0, 0);
graphene_point_init (&p[1].pt, 50, 0);
graphene_point_init (&p[2].pt, 50, 0);
graphene_point_init (&p[3].pt, 100, 0);
gsk_curve_init (&c, gsk_pathop_encode (GSK_PATH_CUBIC, p));
gsk_curve_get_start_tangent (&c, &t);

View File

@ -17,46 +17,46 @@ init_random_curve_with_op (GskCurve *curve,
{
case GSK_PATH_LINE:
{
graphene_point_t p[2];
GskAlignedPoint p[2];
init_random_point (&p[0]);
init_random_point (&p[1]);
init_random_point (&p[0].pt);
init_random_point (&p[1].pt);
gsk_curve_init (curve, gsk_pathop_encode (GSK_PATH_LINE, p));
}
break;
case GSK_PATH_QUAD:
{
graphene_point_t p[3];
GskAlignedPoint p[3];
init_random_point (&p[0]);
init_random_point (&p[1]);
init_random_point (&p[2]);
init_random_point (&p[0].pt);
init_random_point (&p[1].pt);
init_random_point (&p[2].pt);
gsk_curve_init (curve, gsk_pathop_encode (GSK_PATH_QUAD, p));
}
break;
case GSK_PATH_CUBIC:
{
graphene_point_t p[4];
GskAlignedPoint p[4];
init_random_point (&p[0]);
init_random_point (&p[1]);
init_random_point (&p[2]);
init_random_point (&p[3]);
init_random_point (&p[0].pt);
init_random_point (&p[1].pt);
init_random_point (&p[2].pt);
init_random_point (&p[3].pt);
gsk_curve_init (curve, gsk_pathop_encode (GSK_PATH_CUBIC, p));
}
break;
case GSK_PATH_CONIC:
{
graphene_point_t p[4];
GskAlignedPoint p[4];
init_random_point (&p[0]);
init_random_point (&p[1]);
p[2].x = g_test_rand_double_range (0.2, 20);
p[2].y = 0.f;
init_random_point (&p[3]);
init_random_point (&p[0].pt);
init_random_point (&p[1].pt);
p[2].pt.x = g_test_rand_double_range (0.2, 20);
p[2].pt.y = 0.f;
init_random_point (&p[3].pt);
gsk_curve_init (curve, gsk_pathop_encode (GSK_PATH_CONIC, p));
}
break;