gpu: Implement transform support for dihedral transforms

This allows handling them without ever needing to offscreen for losing
the clip, because the clip can always be transformed.

Also, all the optimizations keep working, like occlusion culling,
clears, and so on.

The main benefit of this work is the ability for offloading to now
handle dihedral transforms of the video buffer.

The other big advantage is that we can now start our rendering with a
dihedral transform from the compositor.
This commit is contained in:
Benjamin Otte 2024-07-09 21:07:15 +02:00
parent b9ecae84f5
commit ae3efb2d2f
7 changed files with 101 additions and 13 deletions

View File

@ -59,3 +59,9 @@ gdk_dihedral_combine (GdkDihedral first,
((((first & 3) * (((second & 4) >> 1) + 1)) + second) & 3); ((((first & 3) * (((second & 4) >> 1) + 1)) + second) & 3);
} }
GdkDihedral
gdk_dihedral_invert (GdkDihedral self)
{
return ((4 - self) * (((self & 4) >> 1) + 1) & 3) | (self & 4);
}

View File

@ -40,6 +40,7 @@ void gdk_dihedral_get_mat2 (GdkDihedral
GdkDihedral gdk_dihedral_combine (GdkDihedral first, GdkDihedral gdk_dihedral_combine (GdkDihedral first,
GdkDihedral second); GdkDihedral second);
GdkDihedral gdk_dihedral_invert (GdkDihedral self);
G_END_DECLS G_END_DECLS

View File

@ -168,12 +168,15 @@ gsk_gpu_clip_intersect_rounded_rect (GskGpuClip *dest,
void void
gsk_gpu_clip_scale (GskGpuClip *dest, gsk_gpu_clip_scale (GskGpuClip *dest,
const GskGpuClip *src, const GskGpuClip *src,
GdkDihedral dihedral,
float scale_x, float scale_x,
float scale_y) float scale_y)
{ {
GskRoundedRect tmp;
dest->type = src->type; dest->type = src->type;
gsk_rounded_rect_dihedral (&tmp, &src->rect, dihedral);
gsk_rounded_rect_scale_affine (&dest->rect, gsk_rounded_rect_scale_affine (&dest->rect,
&src->rect, &tmp,
1.0f / scale_x, 1.0f / scale_y, 1.0f / scale_x, 1.0f / scale_y,
0, 0); 0, 0);
} }

View File

@ -6,6 +6,8 @@
#include <graphene.h> #include <graphene.h>
#include <gsk/gskroundedrect.h> #include <gsk/gskroundedrect.h>
#include "gdk/gdkdihedralprivate.h"
G_BEGIN_DECLS G_BEGIN_DECLS
typedef enum { typedef enum {
@ -51,12 +53,13 @@ gboolean gsk_gpu_clip_intersect_rect (GskGpuC
gboolean gsk_gpu_clip_intersect_rounded_rect (GskGpuClip *dest, gboolean gsk_gpu_clip_intersect_rounded_rect (GskGpuClip *dest,
const GskGpuClip *src, const GskGpuClip *src,
const GskRoundedRect *rounded) G_GNUC_WARN_UNUSED_RESULT; const GskRoundedRect *rounded) G_GNUC_WARN_UNUSED_RESULT;
void gsk_gpu_clip_scale (GskGpuClip *dest, void gsk_gpu_clip_scale (GskGpuClip *dest,
const GskGpuClip *src, const GskGpuClip *src,
GdkDihedral dihedral,
float scale_x, float scale_x,
float scale_y); float scale_y);
gboolean gsk_gpu_clip_transform (GskGpuClip *dest, gboolean gsk_gpu_clip_transform (GskGpuClip *dest,
const GskGpuClip *src, const GskGpuClip *src,
GskTransform *transform, GskTransform *transform,
const graphene_rect_t *viewport) G_GNUC_WARN_UNUSED_RESULT; const graphene_rect_t *viewport) G_GNUC_WARN_UNUSED_RESULT;

View File

@ -1165,6 +1165,22 @@ gsk_gpu_node_processor_add_first_rounded_clip_node (GskGpuNodeProcessor
gsk_rounded_clip_node_get_child (node)); gsk_rounded_clip_node_get_child (node));
} }
static GskTransform *
gsk_transform_dihedral (GskTransform *transform,
GdkDihedral dihedral)
{
int rotate = dihedral & 3;
int flip = dihedral & 4;
if (flip)
transform = gsk_transform_scale (transform, -1.0, 1.0);
if (rotate)
transform = gsk_transform_rotate (transform, rotate * 90.0f);
return transform;
}
static void static void
gsk_gpu_node_processor_add_transform_node (GskGpuNodeProcessor *self, gsk_gpu_node_processor_add_transform_node (GskGpuNodeProcessor *self,
GskRenderNode *node) GskRenderNode *node)
@ -1205,7 +1221,7 @@ gsk_gpu_node_processor_add_transform_node (GskGpuNodeProcessor *self,
old_modelview = gsk_transform_ref (self->modelview); old_modelview = gsk_transform_ref (self->modelview);
gsk_transform_to_affine (transform, &scale_x, &scale_y, &dx, &dy); gsk_transform_to_affine (transform, &scale_x, &scale_y, &dx, &dy);
gsk_gpu_clip_scale (&self->clip, &old_clip, scale_x, scale_y); gsk_gpu_clip_scale (&self->clip, &old_clip, GDK_DIHEDRAL_NORMAL, scale_x, scale_y);
self->offset.x = (self->offset.x + dx) / scale_x; self->offset.x = (self->offset.x + dx) / scale_x;
self->offset.y = (self->offset.y + dy) / scale_y; self->offset.y = (self->offset.y + dy) / scale_y;
graphene_vec2_init (&self->scale, fabs (scale_x), fabs (scale_y)); graphene_vec2_init (&self->scale, fabs (scale_x), fabs (scale_y));
@ -1217,6 +1233,32 @@ gsk_gpu_node_processor_add_transform_node (GskGpuNodeProcessor *self,
break; break;
case GSK_FINE_TRANSFORM_CATEGORY_2D_DIHEDRAL: case GSK_FINE_TRANSFORM_CATEGORY_2D_DIHEDRAL:
{
GdkDihedral dihedral, inverted;
float xx, xy, yx, yy, dx, dy, scale_x, scale_y, old_scale_x, old_scale_y;
gsk_gpu_clip_init_copy (&old_clip, &self->clip);
old_offset = self->offset;
old_scale = self->scale;
old_modelview = gsk_transform_ref (self->modelview);
gsk_transform_to_dihedral (transform, &dihedral, &scale_x, &scale_y, &dx, &dy);
inverted = gdk_dihedral_invert (dihedral);
gdk_dihedral_get_mat2 (inverted, &xx, &xy, &yx, &yy);
gsk_gpu_clip_scale (&self->clip, &old_clip, inverted, scale_x, scale_y);
self->offset.x = (self->offset.x + dx) / scale_x;
self->offset.y = (self->offset.y + dy) / scale_y;
self->offset = GRAPHENE_POINT_INIT (xx * self->offset.x + xy * self->offset.y,
yx * self->offset.x + yy * self->offset.y);
old_scale_x = graphene_vec2_get_x (&old_scale);
old_scale_y = graphene_vec2_get_y (&old_scale);
graphene_vec2_init (&self->scale,
fabs (scale_x * (old_scale_x * xx + old_scale_y * yx)),
fabs (scale_y * (old_scale_x * xy + old_scale_y * yy)));
self->modelview = gsk_transform_dihedral (self->modelview, dihedral);
}
break;
case GSK_FINE_TRANSFORM_CATEGORY_2D: case GSK_FINE_TRANSFORM_CATEGORY_2D:
case GSK_FINE_TRANSFORM_CATEGORY_UNKNOWN: case GSK_FINE_TRANSFORM_CATEGORY_UNKNOWN:
case GSK_FINE_TRANSFORM_CATEGORY_ANY: case GSK_FINE_TRANSFORM_CATEGORY_ANY:
@ -1350,7 +1392,7 @@ gsk_gpu_node_processor_add_first_transform_node (GskGpuNodeProcessor *se
old_offset = self->offset; old_offset = self->offset;
old_scale = self->scale; old_scale = self->scale;
gsk_gpu_clip_scale (&self->clip, &old_clip, scale_x, scale_y); gsk_gpu_clip_scale (&self->clip, &old_clip, GDK_DIHEDRAL_NORMAL, scale_x, scale_y);
self->offset.x = (self->offset.x + dx) / scale_x; self->offset.x = (self->offset.x + dx) / scale_x;
self->offset.y = (self->offset.y + dy) / scale_y; self->offset.y = (self->offset.y + dy) / scale_y;
graphene_vec2_init (&self->scale, fabs (scale_x), fabs (scale_y)); graphene_vec2_init (&self->scale, fabs (scale_x), fabs (scale_y));

View File

@ -321,6 +321,34 @@ gsk_rounded_rect_scale_affine (GskRoundedRect *dest,
} }
} }
void
gsk_rounded_rect_dihedral (GskRoundedRect *dest,
const GskRoundedRect *src,
GdkDihedral dihedral)
{
guint flip = (dihedral & 2) + (dihedral >> 2);
guint i;
gsk_rect_dihedral (&src->bounds, dihedral, &dest->bounds);
if (dihedral & 1)
{
for (i = 0; i < 4; i++)
{
dest->corner[i].width = src->corner[((i + 1) & 3) ^ flip].width;
dest->corner[i].height = src->corner[((i + 1) & 3) ^ flip].height;
}
}
else
{
for (i = 0; i < 4; i++)
{
dest->corner[i].width = src->corner[i ^ flip].height;
dest->corner[i].height = src->corner[i ^ flip].width;
}
}
}
/*<private> /*<private>
* gsk_rounded_rect_is_circular: * gsk_rounded_rect_is_circular:
* @self: the `GskRoundedRect` to check * @self: the `GskRoundedRect` to check

View File

@ -2,6 +2,8 @@
#include "gskroundedrect.h" #include "gskroundedrect.h"
#include "gdk/gdkdihedralprivate.h"
#include <cairo.h> #include <cairo.h>
G_BEGIN_DECLS G_BEGIN_DECLS
@ -34,12 +36,15 @@ G_STATIC_ASSERT (OPPOSITE_CORNER_Y (GSK_CORNER_BOTTOM_RIGHT) == GSK_CORNER_TOP_R
}} }}
void gsk_rounded_rect_scale_affine (GskRoundedRect *dest, void gsk_rounded_rect_scale_affine (GskRoundedRect *dest,
const GskRoundedRect *src, const GskRoundedRect *src,
float scale_x, float scale_x,
float scale_y, float scale_y,
float dx, float dx,
float dy); float dy);
void gsk_rounded_rect_dihedral (GskRoundedRect *dest,
const GskRoundedRect *src,
GdkDihedral dihedral);
gboolean gsk_rounded_rect_is_circular (const GskRoundedRect *self) G_GNUC_PURE; gboolean gsk_rounded_rect_is_circular (const GskRoundedRect *self) G_GNUC_PURE;