[gtk/wip/chergert/gsk-gl-texture-library: 1/2] gsk/gl: make texture libraries more autonomous
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/wip/chergert/gsk-gl-texture-library: 1/2] gsk/gl: make texture libraries more autonomous
- Date: Fri, 18 Mar 2022 21:59:56 +0000 (UTC)
commit c64836e1c99e830007d0e646314beb08aef00deb
Author: Christian Hergert <chergert redhat com>
Date: Fri Mar 18 14:51:41 2022 -0700
gsk/gl: make texture libraries more autonomous
This moves a lot of the texture atlas control out of the driver and into
the various texture libraries through their base GskGLTextureLibrary class.
Additionally, this gives more control to libraries on allocating which can
be necessary for some tooling such as future Glyphy integration.
As part of this, the 1x1 pixel initialization is moved to the Glyph library
which is the only place where it is actually needed.
The compact vfunc now is responsible for compaction and it allows for us
to iterate the atlas hashtable a single time instead of twice as we were
doing previously.
The init_atlas vfunc is used to do per-library initialization such as
adding a 1x1 pixel in the Glyph cache used for coloring lines.
The allocate vfunc purely allocates but does no upload. This can be useful
for situations where a library wants to reuse the allocator from the
base class but does not want to actually insert a key/value entry. The
glyph library uses this for it's 1x1 pixel.
In the future, we will also likely want to decouple the rectangle packing
implementation from the atlas structure, or at least move it into a union
so that we do not allocate unused memory for alternate allocators.
gsk/gl/gskgldriver.c | 111 ++-------
gsk/gl/gskgldriverprivate.h | 5 -
gsk/gl/gskglglyphlibrary.c | 50 ++++
gsk/gl/gskgltexturelibrary.c | 466 +++++++++++++++++++++++-------------
gsk/gl/gskgltexturelibraryprivate.h | 64 +++--
5 files changed, 407 insertions(+), 289 deletions(-)
---
diff --git a/gsk/gl/gskgldriver.c b/gsk/gl/gskgldriver.c
index 0980eba0ae..68311b7238 100644
--- a/gsk/gl/gskgldriver.c
+++ b/gsk/gl/gskgldriver.c
@@ -44,8 +44,6 @@
#include <gdk/gdkprofilerprivate.h>
#include <gdk/gdktextureprivate.h>
-#define MAX_OLD_RATIO 0.5
-
G_DEFINE_TYPE (GskGLDriver, gsk_gl_driver, G_TYPE_OBJECT)
static guint
@@ -155,54 +153,6 @@ gsk_gl_driver_collect_unused_textures (GskGLDriver *self,
return collected;
}
-static void
-gsk_gl_texture_atlas_free (GskGLTextureAtlas *atlas)
-{
- if (atlas->texture_id != 0)
- {
- glDeleteTextures (1, &atlas->texture_id);
- atlas->texture_id = 0;
- }
-
- g_clear_pointer (&atlas->nodes, g_free);
- g_slice_free (GskGLTextureAtlas, atlas);
-}
-
-GskGLTextureAtlas *
-gsk_gl_driver_create_atlas (GskGLDriver *self,
- guint width,
- guint height)
-{
- GskGLTextureAtlas *atlas;
-
- g_return_val_if_fail (GSK_IS_GL_DRIVER (self), NULL);
- g_return_val_if_fail (width > 0, NULL);
- g_return_val_if_fail (height > 0, NULL);
-
- atlas = g_slice_new0 (GskGLTextureAtlas);
- atlas->width = width;
- atlas->height = height;
- /* TODO: We might want to change the strategy about the amount of
- * nodes here? stb_rect_pack.h says width is optimal. */
- atlas->nodes = g_malloc0_n (atlas->width, sizeof (struct stbrp_node));
- stbrp_init_target (&atlas->context, atlas->width, atlas->height, atlas->nodes, atlas->width);
- atlas->texture_id = gsk_gl_command_queue_create_texture (self->command_queue,
- atlas->width,
- atlas->height,
- GL_RGBA8,
- GL_LINEAR,
- GL_LINEAR);
-
- gdk_gl_context_label_object_printf (gdk_gl_context_get_current (),
- GL_TEXTURE, atlas->texture_id,
- "Texture atlas %d",
- atlas->texture_id);
-
- g_ptr_array_add (self->atlases, atlas);
-
- return atlas;
-}
-
static void
remove_program (gpointer data)
{
@@ -327,7 +277,6 @@ gsk_gl_driver_dispose (GObject *object)
g_clear_object (&self->icons);
g_clear_object (&self->shadows);
- g_clear_pointer (&self->atlases, g_ptr_array_unref);
g_clear_pointer (&self->autorelease_framebuffers, g_array_unref);
g_clear_pointer (&self->key_to_texture_id, g_hash_table_unref);
g_clear_pointer (&self->textures, g_hash_table_unref);
@@ -364,7 +313,6 @@ gsk_gl_driver_init (GskGLDriver *self)
self->shader_cache = g_hash_table_new_full (NULL, NULL, NULL, remove_program);
self->texture_pool = g_array_new (FALSE, FALSE, sizeof (guint));
self->render_targets = g_ptr_array_new ();
- self->atlases = g_ptr_array_new_with_free_func ((GDestroyNotify)gsk_gl_texture_atlas_free);
}
static gboolean
@@ -575,37 +523,6 @@ failure:
return g_steal_pointer (&driver);
}
-static GPtrArray *
-gsk_gl_driver_compact_atlases (GskGLDriver *self)
-{
- GPtrArray *removed = NULL;
-
- g_assert (GSK_IS_GL_DRIVER (self));
-
- for (guint i = self->atlases->len; i > 0; i--)
- {
- GskGLTextureAtlas *atlas = g_ptr_array_index (self->atlases, i - 1);
-
- if (gsk_gl_texture_atlas_get_unused_ratio (atlas) > MAX_OLD_RATIO)
- {
- GSK_NOTE (GLYPH_CACHE,
- g_message ("Dropping atlas %d (%g.2%% old)", i,
- 100.0 * gsk_gl_texture_atlas_get_unused_ratio (atlas)));
- if (removed == NULL)
- removed = g_ptr_array_new_with_free_func ((GDestroyNotify)gsk_gl_texture_atlas_free);
- g_ptr_array_add (removed, g_ptr_array_steal_index (self->atlases, i - 1));
- }
- }
-
- GSK_NOTE (GLYPH_CACHE, {
- static guint timestamp;
- if (timestamp++ % 60 == 0)
- g_message ("%d atlases", self->atlases->len);
- });
-
- return removed;
-}
-
/**
* gsk_gl_driver_begin_frame:
* @self: a `GskGLDriver`
@@ -622,7 +539,6 @@ gsk_gl_driver_begin_frame (GskGLDriver *self,
GskGLCommandQueue *command_queue)
{
gint64 last_frame_id;
- GPtrArray *removed;
g_return_if_fail (GSK_IS_GL_DRIVER (self));
g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (command_queue));
@@ -637,16 +553,11 @@ gsk_gl_driver_begin_frame (GskGLDriver *self,
gsk_gl_command_queue_begin_frame (self->command_queue);
- /* Compact atlases with too many freed pixels */
- removed = gsk_gl_driver_compact_atlases (self);
-
/* Mark unused pixel regions of the atlases */
gsk_gl_texture_library_begin_frame (GSK_GL_TEXTURE_LIBRARY (self->icons),
- self->current_frame_id,
- removed);
+ self->current_frame_id);
gsk_gl_texture_library_begin_frame (GSK_GL_TEXTURE_LIBRARY (self->glyphs_library),
- self->current_frame_id,
- removed);
+ self->current_frame_id);
/* Cleanup old shadows */
gsk_gl_shadow_library_begin_frame (self->shadows);
@@ -657,9 +568,6 @@ gsk_gl_driver_begin_frame (GskGLDriver *self,
* we block on any resources while delivering our frames.
*/
gsk_gl_driver_collect_unused_textures (self, last_frame_id - 1);
-
- /* Now free atlas textures */
- g_clear_pointer (&removed, g_ptr_array_unref);
}
/**
@@ -1239,14 +1147,23 @@ void
gsk_gl_driver_save_atlases_to_png (GskGLDriver *self,
const char *directory)
{
+ GPtrArray *atlases;
+
g_return_if_fail (GSK_IS_GL_DRIVER (self));
if (directory == NULL)
directory = ".";
- for (guint i = 0; i < self->atlases->len; i++)
+#define copy_atlases(dst, library) \
+ g_ptr_array_extend(dst, GSK_GL_TEXTURE_LIBRARY(library)->atlases, NULL, NULL)
+ atlases = g_ptr_array_new ();
+ copy_atlases (atlases, self->glyphs_library);
+ copy_atlases (atlases, self->icons);
+#undef copy_atlases
+
+ for (guint i = 0; i < atlases->len; i++)
{
- GskGLTextureAtlas *atlas = g_ptr_array_index (self->atlases, i);
+ GskGLTextureAtlas *atlas = g_ptr_array_index (atlases, i);
char *filename = g_strdup_printf ("%s%sframe-%d-atlas-%d.png",
directory,
G_DIR_SEPARATOR_S,
@@ -1255,6 +1172,8 @@ gsk_gl_driver_save_atlases_to_png (GskGLDriver *self,
write_atlas_to_png (self, atlas, filename);
g_free (filename);
}
+
+ g_ptr_array_unref (atlases);
}
#endif
diff --git a/gsk/gl/gskgldriverprivate.h b/gsk/gl/gskgldriverprivate.h
index b1c100f6f7..863240fc3e 100644
--- a/gsk/gl/gskgldriverprivate.h
+++ b/gsk/gl/gskgldriverprivate.h
@@ -109,8 +109,6 @@ struct _GskGLDriver
GHashTable *key_to_texture_id;
GHashTable *texture_id_to_key;
- GPtrArray *atlases;
-
GHashTable *shader_cache;
GArray *autorelease_framebuffers;
@@ -184,9 +182,6 @@ void gsk_gl_driver_add_texture_slices (GskGLDriver *s
GskGLProgram * gsk_gl_driver_lookup_shader (GskGLDriver *self,
GskGLShader *shader,
GError **error);
-GskGLTextureAtlas * gsk_gl_driver_create_atlas (GskGLDriver *self,
- guint width,
- guint height);
#ifdef G_ENABLE_DEBUG
void gsk_gl_driver_save_atlases_to_png (GskGLDriver *self,
diff --git a/gsk/gl/gskglglyphlibrary.c b/gsk/gl/gskglglyphlibrary.c
index ee13198921..58e34bc938 100644
--- a/gsk/gl/gskglglyphlibrary.c
+++ b/gsk/gl/gskglglyphlibrary.c
@@ -93,6 +93,55 @@ gsk_gl_glyph_library_clear_cache (GskGLTextureLibrary *library)
memset (self->front, 0, sizeof self->front);
}
+static void
+gsk_gl_glyph_library_init_atlas (GskGLTextureLibrary *self,
+ GskGLTextureAtlas *atlas)
+{
+ gboolean packed G_GNUC_UNUSED;
+ int x, y;
+ guint gl_format;
+ guint gl_type;
+ guint8 pixel_data[4 * 3 * 3];
+
+ g_assert (GSK_IS_GL_GLYPH_LIBRARY (self));
+ g_assert (atlas != NULL);
+
+ /* Insert a single pixel at 0,0 for use in coloring */
+
+ gdk_gl_context_push_debug_group_printf (gdk_gl_context_get_current (),
+ "Initializing Atlas");
+
+ packed = gsk_gl_texture_library_allocate (self, atlas, 3, 3, &x, &y);
+ g_assert (packed);
+ g_assert (x == 0 && y == 0);
+
+ memset (pixel_data, 255, sizeof pixel_data);
+
+ if (gdk_gl_context_get_use_es (gdk_gl_context_get_current ()))
+ {
+ gl_format = GL_RGBA;
+ gl_type = GL_UNSIGNED_BYTE;
+ }
+ else
+ {
+ gl_format = GL_BGRA;
+ gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
+ }
+
+ glBindTexture (GL_TEXTURE_2D, atlas->texture_id);
+
+ glTexSubImage2D (GL_TEXTURE_2D, 0,
+ 0, 0,
+ 3, 3,
+ gl_format, gl_type,
+ pixel_data);
+
+ gdk_gl_context_pop_debug_group (gdk_gl_context_get_current ());
+
+ self->driver->command_queue->n_uploads++;
+}
+
+
static void
gsk_gl_glyph_library_finalize (GObject *object)
{
@@ -112,6 +161,7 @@ gsk_gl_glyph_library_class_init (GskGLGlyphLibraryClass *klass)
object_class->finalize = gsk_gl_glyph_library_finalize;
library_class->clear_cache = gsk_gl_glyph_library_clear_cache;
+ library_class->init_atlas = gsk_gl_glyph_library_init_atlas;
}
static void
diff --git a/gsk/gl/gskgltexturelibrary.c b/gsk/gl/gskgltexturelibrary.c
index 649f422b0d..3e6463a848 100644
--- a/gsk/gl/gskgltexturelibrary.c
+++ b/gsk/gl/gskgltexturelibrary.c
@@ -30,6 +30,7 @@
#define DEFAULT_MAX_FRAME_AGE 60
#define DEFAULT_ATLAS_WIDTH 512
#define DEFAULT_ATLAS_HEIGHT 512
+#define MAX_OLD_RATIO 0.5
G_DEFINE_ABSTRACT_TYPE (GskGLTextureLibrary, gsk_gl_texture_library, G_TYPE_OBJECT)
@@ -41,6 +42,143 @@ enum {
static GParamSpec *properties [N_PROPS];
+static void
+gsk_gl_texture_atlas_free (GskGLTextureAtlas *atlas)
+{
+ if (atlas->texture_id != 0)
+ {
+ glDeleteTextures (1, &atlas->texture_id);
+ atlas->texture_id = 0;
+ }
+
+ g_clear_pointer (&atlas->nodes, g_free);
+ g_slice_free (GskGLTextureAtlas, atlas);
+}
+
+static gboolean
+gsk_gl_texture_library_real_compact (GskGLTextureLibrary *self,
+ gint64 frame_id)
+{
+ GPtrArray *removed = NULL;
+ gboolean ret = FALSE;
+ gboolean periodic_scan;
+
+ g_assert (GSK_IS_GL_TEXTURE_LIBRARY (self));
+
+ periodic_scan = (self->max_frame_age > 0 &&
+ (frame_id % self->max_frame_age) == 0);
+
+ for (guint i = self->atlases->len; i > 0; i--)
+ {
+ GskGLTextureAtlas *atlas = g_ptr_array_index (self->atlases, i - 1);
+
+ if (gsk_gl_texture_atlas_get_unused_ratio (atlas) > MAX_OLD_RATIO)
+ {
+ GSK_NOTE (GLYPH_CACHE,
+ g_message ("Dropping atlas %d (%g.2%% old)", i,
+ 100.0 * gsk_gl_texture_atlas_get_unused_ratio (atlas)));
+ if (removed == NULL)
+ removed = g_ptr_array_new_with_free_func ((GDestroyNotify)gsk_gl_texture_atlas_free);
+ g_ptr_array_add (removed, g_ptr_array_steal_index (self->atlases, i - 1));
+ }
+ }
+
+ if (periodic_scan || removed != NULL)
+ {
+ GskGLTextureAtlasEntry *entry;
+ GHashTableIter iter;
+ guint dropped = 0;
+ guint atlased = 0;
+
+ g_hash_table_iter_init (&iter, self->hash_table);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&entry))
+ {
+ if (entry->is_atlased)
+ {
+ if (removed && g_ptr_array_find (removed, entry->atlas, NULL))
+ {
+ g_hash_table_iter_remove (&iter);
+ dropped++;
+ }
+ else if (periodic_scan)
+ {
+ gsk_gl_texture_atlas_entry_mark_unused (entry);
+ entry->accessed = FALSE;
+ if (entry->is_atlased)
+ atlased++;
+ }
+ }
+ else if (!entry->accessed)
+ {
+ gsk_gl_driver_release_texture (self->driver, entry->texture);
+ g_hash_table_iter_remove (&iter);
+ dropped++;
+ }
+ }
+
+ GSK_NOTE (GLYPH_CACHE, g_message ("%s: Dropped %d individual items",
+ G_OBJECT_TYPE_NAME (self),
+ dropped);
+ g_message ("%s: %d items cached (%d atlased, %d individually)",
+ G_OBJECT_TYPE_NAME (self),
+ g_hash_table_size (self->hash_table),
+ atlased,
+ g_hash_table_size (self->hash_table) - atlased));
+
+ if (dropped > 0)
+ gsk_gl_texture_library_clear_cache (self);
+
+ ret = TRUE;
+
+ g_clear_pointer (&removed, g_ptr_array_unref);
+ }
+
+ GSK_NOTE (GLYPH_CACHE, {
+ static gint64 last_message;
+ gint64 now = g_get_monotonic_time ();
+ if (now - last_message > G_USEC_PER_SEC)
+ {
+ last_message = now;
+ g_message ("%s contains %d atlases",
+ G_OBJECT_TYPE_NAME (self),
+ self->atlases->len);
+ }
+ });
+
+ return ret;
+}
+
+static gboolean
+gsk_gl_texture_library_real_allocate (GskGLTextureLibrary *self,
+ GskGLTextureAtlas *atlas,
+ int width,
+ int height,
+ int *out_x,
+ int *out_y)
+{
+ stbrp_rect rect;
+
+ g_assert (GSK_IS_GL_TEXTURE_LIBRARY (self));
+ g_assert (atlas != NULL);
+ g_assert (width > 0);
+ g_assert (height > 0);
+ g_assert (out_x != NULL);
+ g_assert (out_y != NULL);
+
+ rect.w = width;
+ rect.h = height;
+
+ stbrp_pack_rects (&atlas->context, &rect, 1);
+
+ if (rect.was_packed)
+ {
+ *out_x = rect.x;
+ *out_y = rect.y;
+ }
+
+ return rect.was_packed;
+}
+
static void
gsk_gl_texture_library_constructed (GObject *object)
{
@@ -108,6 +246,9 @@ gsk_gl_texture_library_class_init (GskGLTextureLibraryClass *klass)
object_class->get_property = gsk_gl_texture_library_get_property;
object_class->set_property = gsk_gl_texture_library_set_property;
+ klass->compact = gsk_gl_texture_library_real_compact;
+ klass->allocate = gsk_gl_texture_library_real_allocate;
+
properties [PROP_DRIVER] =
g_param_spec_object ("driver",
"Driver",
@@ -124,7 +265,7 @@ gsk_gl_texture_library_init (GskGLTextureLibrary *self)
self->max_frame_age = DEFAULT_MAX_FRAME_AGE;
self->atlas_width = DEFAULT_ATLAS_WIDTH;
self->atlas_height = DEFAULT_ATLAS_HEIGHT;
- self->atlases = g_ptr_array_new ();
+ self->atlases = g_ptr_array_new_with_free_func ((GDestroyNotify)gsk_gl_texture_atlas_free);
}
void
@@ -143,90 +284,14 @@ gsk_gl_texture_library_set_funcs (GskGLTextureLibrary *self,
void
gsk_gl_texture_library_begin_frame (GskGLTextureLibrary *self,
- gint64 frame_id,
- GPtrArray *removed_atlases)
+ gint64 frame_id)
{
- GHashTableIter iter;
- gboolean drop_caches = FALSE;
-
g_return_if_fail (GSK_IS_GL_TEXTURE_LIBRARY (self));
- if (GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->begin_frame)
- GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->begin_frame (self, frame_id, removed_atlases);
-
- if (removed_atlases != NULL)
- {
- GskGLTextureAtlasEntry *entry;
- guint dropped = 0;
-
- /* Remove cached copy of purged atlases */
- for (guint i = 0; i < removed_atlases->len; i++)
- g_ptr_array_remove (self->atlases, g_ptr_array_index (removed_atlases, i));
-
- g_hash_table_iter_init (&iter, self->hash_table);
- while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&entry))
- {
- if (entry->is_atlased)
- {
- for (guint i = 0; i < removed_atlases->len; i++)
- {
- GskGLTextureAtlas *atlas = g_ptr_array_index (removed_atlases, i);
-
- if (atlas == entry->atlas)
- {
- g_hash_table_iter_remove (&iter);
- dropped++;
- break;
- }
- }
- }
- }
-
- GSK_NOTE (GLYPH_CACHE,
- if (dropped > 0)
- g_message ("%s: Dropped %d items",
- G_OBJECT_TYPE_NAME (self), dropped));
-
- drop_caches |= dropped > 0;
- }
-
- if (frame_id % self->max_frame_age == 0)
- {
- GskGLTextureAtlasEntry *entry;
- int atlased = 0;
- int dropped = 0;
-
- g_hash_table_iter_init (&iter, self->hash_table);
- while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&entry))
- {
- if (!entry->is_atlased && !entry->accessed)
- {
- gsk_gl_driver_release_texture (self->driver, entry->texture);
- g_hash_table_iter_remove (&iter);
- dropped++;
- continue;
- }
-
- gsk_gl_texture_atlas_entry_mark_unused (entry);
- entry->accessed = FALSE;
- if (entry->is_atlased)
- atlased++;
- }
-
- GSK_NOTE (GLYPH_CACHE, g_message ("%s: Dropped %d individual items",
- G_OBJECT_TYPE_NAME (self),
- dropped);
- g_message ("%s: %d items cached (%d atlased, %d individually)",
- G_OBJECT_TYPE_NAME (self),
- g_hash_table_size (self->hash_table),
- atlased,
- g_hash_table_size (self->hash_table) - atlased));
-
- drop_caches |= dropped > 0;
- }
+ gsk_gl_texture_library_compact (self, frame_id);
- if (drop_caches && GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->clear_cache)
- GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->clear_cache (self);
+ if (GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->begin_frame)
+ GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->begin_frame (self, frame_id);
}
static GskGLTexture *
@@ -253,92 +318,29 @@ gsk_gl_texture_library_pack_one (GskGLTextureLibrary *self,
return texture;
}
-static inline gboolean
-gsk_gl_texture_atlas_pack (GskGLTextureAtlas *self,
- int width,
- int height,
- int *out_x,
- int *out_y)
-{
- stbrp_rect rect;
-
- rect.w = width;
- rect.h = height;
-
- stbrp_pack_rects (&self->context, &rect, 1);
-
- if (rect.was_packed)
- {
- *out_x = rect.x;
- *out_y = rect.y;
- }
-
- return rect.was_packed;
-}
-
-static void
-gsk_gl_texture_library_init_atlas (GskGLTextureLibrary *self,
- GskGLTextureAtlas *atlas)
-{
- /* Insert a single pixel at 0,0 for use in coloring */
-
- gboolean packed G_GNUC_UNUSED;
- int x, y;
- guint gl_format;
- guint gl_type;
- guint8 pixel_data[4 * 3 * 3];
-
- g_ptr_array_add (self->atlases, atlas);
-
- gdk_gl_context_push_debug_group_printf (gdk_gl_context_get_current (),
- "Initializing Atlas");
-
- packed = gsk_gl_texture_atlas_pack (atlas, 3, 3, &x, &y);
- g_assert (packed);
- g_assert (x == 0 && y == 0);
-
- memset (pixel_data, 255, sizeof pixel_data);
-
- if (gdk_gl_context_get_use_es (gdk_gl_context_get_current ()))
- {
- gl_format = GL_RGBA;
- gl_type = GL_UNSIGNED_BYTE;
- }
- else
- {
- gl_format = GL_BGRA;
- gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
- }
-
- glBindTexture (GL_TEXTURE_2D, atlas->texture_id);
-
- glTexSubImage2D (GL_TEXTURE_2D, 0,
- 0, 0,
- 3, 3,
- gl_format, gl_type,
- pixel_data);
-
- gdk_gl_context_pop_debug_group (gdk_gl_context_get_current ());
-
- self->driver->command_queue->n_uploads++;
-}
-
static void
-gsk_gl_texture_atlases_pack (GskGLTextureLibrary *self,
- int width,
- int height,
- GskGLTextureAtlas **out_atlas,
- int *out_x,
- int *out_y)
+gsk_gl_texture_library_pack_any_atlas (GskGLTextureLibrary *self,
+ int width,
+ int height,
+ GskGLTextureAtlas **out_atlas,
+ int *out_x,
+ int *out_y)
{
GskGLTextureAtlas *atlas = NULL;
int x, y;
+ g_assert (GSK_IS_GL_TEXTURE_LIBRARY (self));
+ g_assert (width > 0);
+ g_assert (height > 0);
+ g_assert (out_atlas != NULL);
+ g_assert (out_x != NULL);
+ g_assert (out_y != NULL);
+
for (guint i = 0; i < self->atlases->len; i++)
{
atlas = g_ptr_array_index (self->atlases, i);
- if (gsk_gl_texture_atlas_pack (atlas, width, height, &x, &y))
+ if (gsk_gl_texture_library_allocate (self, atlas, width, height, &x, &y))
break;
atlas = NULL;
@@ -347,11 +349,10 @@ gsk_gl_texture_atlases_pack (GskGLTextureLibrary *self,
if (atlas == NULL)
{
/* No atlas has enough space, so create a new one... */
- atlas = gsk_gl_driver_create_atlas (self->driver, self->atlas_width, self->atlas_height);
- gsk_gl_texture_library_init_atlas (self, atlas);
+ atlas = gsk_gl_texture_library_acquire_atlas (self);
/* Pack it onto that one, which surely has enough space... */
- if (!gsk_gl_texture_atlas_pack (atlas, width, height, &x, &y))
+ if (!gsk_gl_texture_library_allocate (self, atlas, width, height, &x, &y))
g_assert_not_reached ();
}
@@ -407,12 +408,12 @@ gsk_gl_texture_library_pack (GskGLTextureLibrary *self,
int packed_x;
int packed_y;
- gsk_gl_texture_atlases_pack (self,
- padding + width + padding,
- padding + height + padding,
- &atlas,
- &packed_x,
- &packed_y);
+ gsk_gl_texture_library_pack_any_atlas (self,
+ padding + width + padding,
+ padding + height + padding,
+ &atlas,
+ &packed_x,
+ &packed_y);
entry->atlas = atlas;
entry->is_atlased = TRUE;
@@ -446,3 +447,134 @@ gsk_gl_texture_library_pack (GskGLTextureLibrary *self,
return entry;
}
+
+/*
+ * gsk_gl_texture_library_clear_cache:
+ *
+ * Clear the front cache if the texture library is using one. For
+ * example the glyph cache would drop it's front cache to force
+ * next lookups to fall through to the GHashTable key lookup.
+ */
+void
+gsk_gl_texture_library_clear_cache (GskGLTextureLibrary *self)
+{
+ g_return_if_fail (GSK_IS_GL_TEXTURE_LIBRARY (self));
+
+ if (GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->clear_cache)
+ GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->clear_cache (self);
+}
+
+/*
+ * gsk_gl_texture_library_compact:
+ *
+ * Requests that the texture library compact it's altases. That
+ * generally means to traverse them to look for unused pixels over
+ * a certain threshold and release them if necessary.
+ *
+ * Returns: %TRUE if any compaction occurred.
+ */
+gboolean
+gsk_gl_texture_library_compact (GskGLTextureLibrary *self,
+ gint64 frame_id)
+{
+ g_return_val_if_fail (GSK_IS_GL_TEXTURE_LIBRARY (self), FALSE);
+
+ if (GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->compact)
+ return GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->compact (self, frame_id);
+
+ return FALSE;
+}
+
+void
+gsk_gl_texture_library_reset (GskGLTextureLibrary *self)
+{
+ g_return_if_fail (GSK_IS_GL_TEXTURE_LIBRARY (self));
+
+ gsk_gl_texture_library_clear_cache (self);
+
+ g_hash_table_remove_all (self->hash_table);
+
+ if (self->atlases->len)
+ g_ptr_array_remove_range (self->atlases, 0, self->atlases->len);
+}
+
+void
+gsk_gl_texture_library_set_atlas_size (GskGLTextureLibrary *self,
+ int width,
+ int height)
+{
+ g_return_if_fail (GSK_IS_GL_TEXTURE_LIBRARY (self));
+
+ if (width <= 0)
+ width = DEFAULT_ATLAS_WIDTH;
+
+ if (height <= 0)
+ height = DEFAULT_ATLAS_HEIGHT;
+
+ self->atlas_height = height;
+ self->atlas_width = width;
+
+ gsk_gl_texture_library_reset (self);
+}
+
+/*
+ * gsk_gl_texture_library_acquire_atlas:
+ *
+ * Allocates a new texture atlas based on the current size
+ * and format requirements.
+ */
+GskGLTextureAtlas *
+gsk_gl_texture_library_acquire_atlas (GskGLTextureLibrary *self)
+{
+ GskGLTextureAtlas *atlas;
+
+ g_return_val_if_fail (GSK_IS_GL_TEXTURE_LIBRARY (self), NULL);
+ g_return_val_if_fail (GSK_IS_GL_DRIVER (self->driver), NULL);
+ g_return_val_if_fail (GSK_IS_GL_COMMAND_QUEUE (self->driver->command_queue), NULL);
+ g_return_val_if_fail (self->atlas_width > 0, NULL);
+ g_return_val_if_fail (self->atlas_height > 0, NULL);
+
+ atlas = g_slice_new0 (GskGLTextureAtlas);
+ atlas->width = self->atlas_width;
+ atlas->height = self->atlas_height;
+ /* TODO: We might want to change the strategy about the amount of
+ * nodes here? stb_rect_pack.h says width is optimal. */
+ atlas->nodes = g_malloc0_n (atlas->width, sizeof (struct stbrp_node));
+ stbrp_init_target (&atlas->context, atlas->width, atlas->height, atlas->nodes, atlas->width);
+ atlas->texture_id = gsk_gl_command_queue_create_texture (self->driver->command_queue,
+ atlas->width,
+ atlas->height,
+ GL_RGBA8,
+ GL_LINEAR,
+ GL_LINEAR);
+
+ gdk_gl_context_label_object_printf (gdk_gl_context_get_current (),
+ GL_TEXTURE, atlas->texture_id,
+ "Texture atlas %d",
+ atlas->texture_id);
+
+ g_ptr_array_add (self->atlases, atlas);
+
+ if (GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->init_atlas)
+ GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->init_atlas (self, atlas);
+
+ return atlas;
+}
+
+gboolean
+gsk_gl_texture_library_allocate (GskGLTextureLibrary *self,
+ GskGLTextureAtlas *atlas,
+ int width,
+ int height,
+ int *out_x,
+ int *out_y)
+{
+ g_assert (GSK_IS_GL_TEXTURE_LIBRARY (self));
+ g_assert (atlas != NULL);
+ g_assert (width > 0);
+ g_assert (height > 0);
+ g_assert (out_x != NULL);
+ g_assert (out_y != NULL);
+
+ return GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->allocate (self, atlas, width, height, out_x, out_y);
+}
diff --git a/gsk/gl/gskgltexturelibraryprivate.h b/gsk/gl/gskgltexturelibraryprivate.h
index 167b00923b..a0292c497b 100644
--- a/gsk/gl/gskgltexturelibraryprivate.h
+++ b/gsk/gl/gskgltexturelibraryprivate.h
@@ -102,31 +102,53 @@ typedef struct _GskGLTextureLibraryClass
{
GObjectClass parent_class;
- void (*begin_frame) (GskGLTextureLibrary *library,
- gint64 frame_id,
- GPtrArray *removed_atlases);
- void (*clear_cache) (GskGLTextureLibrary *library);
+ void (*begin_frame) (GskGLTextureLibrary *library,
+ gint64 frame_id);
+ gboolean (*compact) (GskGLTextureLibrary *library,
+ gint64 frame_id);
+ void (*clear_cache) (GskGLTextureLibrary *library);
+ void (*init_atlas) (GskGLTextureLibrary *library,
+ GskGLTextureAtlas *atlas);
+ gboolean (*allocate) (GskGLTextureLibrary *library,
+ GskGLTextureAtlas *atlas,
+ int width,
+ int height,
+ int *out_x,
+ int *out_y);
} GskGLTextureLibraryClass;
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GskGLTextureLibrary, g_object_unref)
-GType gsk_gl_texture_library_get_type (void) G_GNUC_CONST;
-void gsk_gl_texture_library_set_funcs (GskGLTextureLibrary *self,
- GHashFunc hash_func,
- GEqualFunc equal_func,
- GDestroyNotify key_destroy,
- GDestroyNotify value_destroy);
-void gsk_gl_texture_library_begin_frame (GskGLTextureLibrary *self,
- gint64 frame_id,
- GPtrArray *removed_atlases);
-gpointer gsk_gl_texture_library_pack (GskGLTextureLibrary *self,
- gpointer key,
- gsize valuelen,
- guint width,
- guint height,
- int padding,
- guint *out_packed_x,
- guint *out_packed_y);
+GType gsk_gl_texture_library_get_type (void) G_GNUC_CONST;
+gboolean gsk_gl_texture_library_compact (GskGLTextureLibrary *self,
+ gint64 frame_id);
+void gsk_gl_texture_library_clear_cache (GskGLTextureLibrary *self);
+void gsk_gl_texture_library_reset (GskGLTextureLibrary *self);
+void gsk_gl_texture_library_set_atlas_size (GskGLTextureLibrary *self,
+ int width,
+ int height);
+GskGLTextureAtlas *gsk_gl_texture_library_acquire_atlas (GskGLTextureLibrary *self);
+void gsk_gl_texture_library_set_funcs (GskGLTextureLibrary *self,
+ GHashFunc hash_func,
+ GEqualFunc equal_func,
+ GDestroyNotify key_destroy,
+ GDestroyNotify value_destroy);
+void gsk_gl_texture_library_begin_frame (GskGLTextureLibrary *self,
+ gint64 frame_id);
+gboolean gsk_gl_texture_library_allocate (GskGLTextureLibrary *self,
+ GskGLTextureAtlas *atlas,
+ int width,
+ int height,
+ int *out_x,
+ int *out_y);
+gpointer gsk_gl_texture_library_pack (GskGLTextureLibrary *self,
+ gpointer key,
+ gsize valuelen,
+ guint width,
+ guint height,
+ int padding,
+ guint *out_packed_x,
+ guint *out_packed_y);
static inline void
gsk_gl_texture_atlas_mark_unused (GskGLTextureAtlas *self,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]