gtk/gsk/gpu/gskgpudevice.c
Benjamin Otte 7d43e4e56a gpu: Add glyphs support
This is very rudimentary, but it's a step in the direction of getting
text going.

There's lots of things missing still.
2024-01-07 07:22:50 +01:00

377 lines
11 KiB
C

#include "config.h"
#include "gskgpudeviceprivate.h"
#include "gskgpuframeprivate.h"
#include "gskgpuuploadopprivate.h"
#include "gdk/gdkdisplayprivate.h"
#include "gdk/gdktextureprivate.h"
typedef enum
{
GSK_GPU_CACHE_TEXTURE,
GSK_GPU_CACHE_GLYPH
} GskGpuCacheType;
typedef struct _GskGpuCachedTexture GskGpuCachedTexture;
typedef struct _GskGpuCachedGlyph GskGpuCachedGlyph;
typedef struct _GskGpuCacheEntry GskGpuCacheEntry;
struct _GskGpuCacheEntry
{
GskGpuCacheType type;
guint64 last_use_timestamp;
};
struct _GskGpuCachedTexture
{
GskGpuCacheEntry entry;
/* atomic */ GdkTexture *texture;
GskGpuImage *image;
};
struct _GskGpuCachedGlyph
{
GskGpuCacheEntry entry;
PangoFont *font;
PangoGlyph glyph;
GskGpuGlyphLookupFlags flags;
float scale;
GskGpuImage *image;
graphene_rect_t bounds;
graphene_point_t origin;
};
#define GDK_ARRAY_NAME gsk_gpu_cache_entries
#define GDK_ARRAY_TYPE_NAME GskGpuCacheEntries
#define GDK_ARRAY_ELEMENT_TYPE GskGpuCacheEntry *
#define GDK_ARRAY_PREALLOC 32
#define GDK_ARRAY_NO_MEMSET 1
#include "gdk/gdkarrayimpl.c"
typedef struct _GskGpuDevicePrivate GskGpuDevicePrivate;
struct _GskGpuDevicePrivate
{
GdkDisplay *display;
GskGpuCacheEntries cache;
guint cache_gc_source;
GHashTable *glyph_cache;
};
G_DEFINE_TYPE_WITH_PRIVATE (GskGpuDevice, gsk_gpu_device, G_TYPE_OBJECT)
static guint
gsk_gpu_cached_glyph_hash (gconstpointer data)
{
const GskGpuCachedGlyph *glyph = data;
return GPOINTER_TO_UINT (glyph->font) ^
glyph->glyph ^
(glyph->flags << 24) ^
((guint) glyph->scale * PANGO_SCALE);
}
static gboolean
gsk_gpu_cached_glyph_equal (gconstpointer v1,
gconstpointer v2)
{
const GskGpuCachedGlyph *glyph1 = v1;
const GskGpuCachedGlyph *glyph2 = v2;
return glyph1->font == glyph2->font
&& glyph1->glyph == glyph2->glyph
&& glyph1->flags == glyph2->flags
&& glyph1->scale == glyph2->scale;
}
void
gsk_gpu_device_gc (GskGpuDevice *self,
gint64 timestamp)
{
GskGpuDevicePrivate *priv = gsk_gpu_device_get_instance_private (self);
gsize i;
for (i = 0; i < gsk_gpu_cache_entries_get_size (&priv->cache); i++)
{
GskGpuCacheEntry *entry = gsk_gpu_cache_entries_get (&priv->cache, i);
switch (entry->type)
{
case GSK_GPU_CACHE_TEXTURE:
{
GskGpuCachedTexture *texture = (GskGpuCachedTexture *) entry;
if (texture->texture != NULL)
continue;
gdk_texture_clear_render_data (texture->texture);
g_object_unref (texture->image);
}
break;
case GSK_GPU_CACHE_GLYPH:
break;
default:
g_assert_not_reached ();
break;
}
if (i + 1 != gsk_gpu_cache_entries_get_size (&priv->cache))
{
*gsk_gpu_cache_entries_index (&priv->cache, i) = *gsk_gpu_cache_entries_index (&priv->cache, gsk_gpu_cache_entries_get_size (&priv->cache) - 1);
gsk_gpu_cache_entries_set_size (&priv->cache, gsk_gpu_cache_entries_get_size (&priv->cache) - 1);
}
i--;
}
}
static void
gsk_gpu_device_clear_cache (GskGpuDevice *self)
{
GskGpuDevicePrivate *priv = gsk_gpu_device_get_instance_private (self);
gsize i;
for (i = 0; i < gsk_gpu_cache_entries_get_size (&priv->cache); i++)
{
GskGpuCacheEntry *entry = gsk_gpu_cache_entries_get (&priv->cache, i);
switch (entry->type)
{
case GSK_GPU_CACHE_TEXTURE:
{
GskGpuCachedTexture *texture = (GskGpuCachedTexture *) entry;
if (texture->texture)
gdk_texture_clear_render_data (texture->texture);
g_object_unref (texture->image);
}
break;
case GSK_GPU_CACHE_GLYPH:
{
GskGpuCachedGlyph *glyph = (GskGpuCachedGlyph *) entry;
g_object_unref (glyph->font);
g_object_unref (glyph->image);
}
break;
default:
g_assert_not_reached ();
break;
}
g_free (entry);
}
gsk_gpu_cache_entries_set_size (&priv->cache, 0);
}
static void
gsk_gpu_device_dispose (GObject *object)
{
GskGpuDevice *self = GSK_GPU_DEVICE (object);
GskGpuDevicePrivate *priv = gsk_gpu_device_get_instance_private (self);
g_hash_table_unref (priv->glyph_cache);
gsk_gpu_device_clear_cache (self);
gsk_gpu_cache_entries_clear (&priv->cache);
G_OBJECT_CLASS (gsk_gpu_device_parent_class)->dispose (object);
}
static void
gsk_gpu_device_finalize (GObject *object)
{
GskGpuDevice *self = GSK_GPU_DEVICE (object);
GskGpuDevicePrivate *priv = gsk_gpu_device_get_instance_private (self);
g_object_unref (priv->display);
G_OBJECT_CLASS (gsk_gpu_device_parent_class)->finalize (object);
}
static void
gsk_gpu_device_class_init (GskGpuDeviceClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = gsk_gpu_device_dispose;
object_class->finalize = gsk_gpu_device_finalize;
}
static void
gsk_gpu_device_init (GskGpuDevice *self)
{
GskGpuDevicePrivate *priv = gsk_gpu_device_get_instance_private (self);
gsk_gpu_cache_entries_init (&priv->cache);
priv->glyph_cache = g_hash_table_new (gsk_gpu_cached_glyph_hash,
gsk_gpu_cached_glyph_equal);
}
void
gsk_gpu_device_setup (GskGpuDevice *self,
GdkDisplay *display)
{
GskGpuDevicePrivate *priv = gsk_gpu_device_get_instance_private (self);
priv->display = g_object_ref (display);
}
GdkDisplay *
gsk_gpu_device_get_display (GskGpuDevice *self)
{
GskGpuDevicePrivate *priv = gsk_gpu_device_get_instance_private (self);
return priv->display;
}
GskGpuImage *
gsk_gpu_device_create_offscreen_image (GskGpuDevice *self,
GdkMemoryDepth depth,
gsize width,
gsize height)
{
return GSK_GPU_DEVICE_GET_CLASS (self)->create_offscreen_image (self, depth, width, height);
}
GskGpuImage *
gsk_gpu_device_create_upload_image (GskGpuDevice *self,
GdkMemoryFormat format,
gsize width,
gsize height)
{
return GSK_GPU_DEVICE_GET_CLASS (self)->create_upload_image (self, format, width, height);
}
GskGpuImage *
gsk_gpu_device_lookup_texture_image (GskGpuDevice *self,
GdkTexture *texture,
gint64 timestamp)
{
GskGpuCachedTexture *cache;
cache = gdk_texture_get_render_data (texture, self);
if (cache)
{
cache->entry.last_use_timestamp = timestamp;
return g_object_ref (cache->image);
}
return NULL;
}
static void
gsk_gpu_device_cache_texture_destroy_cb (gpointer data)
{
GskGpuCachedTexture *cache = data;
g_atomic_pointer_set (&cache->texture, NULL);
}
void
gsk_gpu_device_cache_texture_image (GskGpuDevice *self,
GdkTexture *texture,
gint64 timestamp,
GskGpuImage *image)
{
GskGpuDevicePrivate *priv = gsk_gpu_device_get_instance_private (self);
GskGpuCachedTexture *cache;
cache = g_new (GskGpuCachedTexture, 1);
*cache = (GskGpuCachedTexture) {
.entry = {
.type = GSK_GPU_CACHE_TEXTURE,
.last_use_timestamp = timestamp,
},
.texture = texture,
.image = g_object_ref (image)
};
if (gdk_texture_get_render_data (texture, self))
gdk_texture_clear_render_data (texture);
gdk_texture_set_render_data (texture, self, cache, gsk_gpu_device_cache_texture_destroy_cb);
gsk_gpu_cache_entries_append (&priv->cache, (GskGpuCacheEntry *) cache);
}
GskGpuImage *
gsk_gpu_device_lookup_glyph_image (GskGpuDevice *self,
GskGpuFrame *frame,
PangoFont *font,
PangoGlyph glyph,
GskGpuGlyphLookupFlags flags,
float scale,
graphene_rect_t *out_bounds,
graphene_point_t *out_origin)
{
GskGpuDevicePrivate *priv = gsk_gpu_device_get_instance_private (self);
GskGpuCachedGlyph lookup = {
.font = font,
.glyph = glyph,
.flags = flags,
.scale = scale
};
GskGpuCachedGlyph *cache;
PangoRectangle ink_rect;
graphene_rect_t rect;
cache = g_hash_table_lookup (priv->glyph_cache, &lookup);
if (cache)
{
cache->entry.last_use_timestamp = gsk_gpu_frame_get_timestamp (frame);
*out_bounds = cache->bounds;
*out_origin = cache->origin;
return cache->image;
}
cache = g_new (GskGpuCachedGlyph, 1);
pango_font_get_glyph_extents (font, glyph, &ink_rect, NULL);
rect.origin.x = floor (ink_rect.x * scale / PANGO_SCALE) - 1;
rect.origin.y = floor (ink_rect.y * scale / PANGO_SCALE) - 1;
rect.size.width = ceil ((ink_rect.x + ink_rect.width) * scale / PANGO_SCALE) - rect.origin.x + 2;
rect.size.height = ceil ((ink_rect.y + ink_rect.height) * scale / PANGO_SCALE) - rect.origin.y + 2;
*cache = (GskGpuCachedGlyph) {
.entry = {
.type = GSK_GPU_CACHE_GLYPH,
.last_use_timestamp = gsk_gpu_frame_get_timestamp (frame),
},
.font = g_object_ref (font),
.glyph = glyph,
.flags = flags,
.scale = scale,
.image = gsk_gpu_device_create_upload_image (self, GDK_MEMORY_DEFAULT, rect.size.width, rect.size.height),
.bounds = GRAPHENE_RECT_INIT (0, 0, rect.size.width, rect.size.height),
.origin = GRAPHENE_POINT_INIT (-rect.origin.x + (flags & 3) / 4.f,
-rect.origin.y + ((flags >> 2) & 3) / 4.f)
};
gsk_gpu_upload_glyph_op (frame,
cache->image,
font,
glyph,
&(cairo_rectangle_int_t) {
.x = 0,
.y = 0,
.width = rect.size.width,
.height = rect.size.height
},
scale,
&cache->origin);
g_hash_table_insert (priv->glyph_cache, cache, cache);
gsk_gpu_cache_entries_append (&priv->cache, (GskGpuCacheEntry *) cache);
*out_bounds = cache->bounds;
*out_origin = cache->origin;
return cache->image;
}