mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-12-25 21:21:21 +00:00
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.
This commit is contained in:
parent
6b23fe3aa7
commit
c64836e1c9
@ -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
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
|
||||
gsk_gl_texture_library_compact (self, frame_id);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (drop_caches && GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->clear_cache)
|
||||
GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->clear_cache (self);
|
||||
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);
|
||||
}
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user