summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTimm Bäder <mail@baedert.org>2019-05-22 09:33:25 +0200
committerTimm Bäder <mail@baedert.org>2019-05-22 10:03:02 +0200
commit1ee38ca02381e534706a9a3fa6c97abe18330830 (patch)
treed460f8c1efcbbdd92a5d3c874b09245f09a4803a
parent0e11f83ec57de0a93119917ba6fff4c682c4ab8e (diff)
downloadgtk+-wip/baedert/stb-rect-pack.tar.gz
gl renderer: Add & use icon cachewip/baedert/stb-rect-pack
Upload small icons all to the same texture atlas.
-rw-r--r--gsk/gl/gskgliconcache.c194
-rw-r--r--gsk/gl/gskgliconcacheprivate.h31
-rw-r--r--gsk/gl/gskglrenderer.c49
-rw-r--r--gsk/meson.build1
4 files changed, 262 insertions, 13 deletions
diff --git a/gsk/gl/gskgliconcache.c b/gsk/gl/gskgliconcache.c
new file mode 100644
index 0000000000..06c8a3ae63
--- /dev/null
+++ b/gsk/gl/gskgliconcache.c
@@ -0,0 +1,194 @@
+#include "gskgliconcacheprivate.h"
+#include "gskgltextureatlasprivate.h"
+#include "gdk/gdktextureprivate.h"
+
+#define ATLAS_SIZE (1024)
+#define MAX_FRAME_AGE (5 * 60)
+#define MAX_UNUSED_RATIO 0.8
+
+typedef struct
+{
+ graphene_rect_t texture_rect;
+ GskGLTextureAtlas *atlas;
+ int frame_age; /* Number of frames this icon is unused */
+ guint used: 1;
+} IconData;
+
+static void
+icon_data_free (gpointer p)
+{
+}
+
+void
+gsk_gl_icon_cache_init (GskGLIconCache *self,
+ GskRenderer *renderer,
+ GskGLDriver *gl_driver)
+{
+ self->renderer = renderer;
+ self->gl_driver = gl_driver;
+
+ self->atlases = g_ptr_array_new ();
+ self->icons = g_hash_table_new_full (NULL, NULL, NULL, icon_data_free);
+}
+
+void
+gsk_gl_icon_cache_free (GskGLIconCache *self)
+{
+ guint i, p;
+
+ for (i = 0, p = self->atlases->len; i < p; i ++)
+ {
+ GskGLTextureAtlas *atlas = g_ptr_array_index (self->atlases, i);
+
+ gsk_gl_texture_atlas_free (atlas);
+ g_free (atlas);
+ }
+ g_ptr_array_free (self->atlases, TRUE);
+
+ g_hash_table_unref (self->icons);
+}
+
+void
+gsk_gl_icon_cache_begin_frame (GskGLIconCache *self)
+{
+ gint i, p;
+ GHashTableIter iter;
+ GdkTexture *texture;
+ IconData *icon_data;
+
+ /* Increase frame age of all icons */
+ g_hash_table_iter_init (&iter, self->icons);
+ while (g_hash_table_iter_next (&iter, (gpointer *)&texture, (gpointer *)&icon_data))
+ {
+ icon_data->frame_age ++;
+
+ if (icon_data->frame_age > MAX_FRAME_AGE)
+ {
+
+ if (icon_data->used)
+ {
+ const int w = icon_data->texture_rect.size.width * ATLAS_SIZE;
+ const int h = icon_data->texture_rect.size.height * ATLAS_SIZE;
+
+ gsk_gl_texture_atlas_mark_unused (icon_data->atlas, w, h);
+ icon_data->used = FALSE;
+ }
+ /* We do NOT remove the icon here. Instead, We wait until we drop the entire atlas.
+ * This way we can revive it when we use it again. */
+ }
+ }
+
+ for (i = 0, p = self->atlases->len; i < p; i ++)
+ {
+ GskGLTextureAtlas *atlas = g_ptr_array_index (self->atlases, i);
+
+ if (gsk_gl_texture_atlas_get_unused_ratio (atlas) > MAX_UNUSED_RATIO)
+ {
+ g_hash_table_iter_init (&iter, self->icons);
+ while (g_hash_table_iter_next (&iter, (gpointer *)&texture, (gpointer *)&icon_data))
+ {
+ if (icon_data->atlas == atlas)
+ g_hash_table_iter_remove (&iter);
+ }
+
+ g_ptr_array_remove_index_fast (self->atlases, i);
+ i --; /* Check the current index again */
+ }
+ }
+}
+
+void
+gsk_gl_icon_cache_lookup_or_add (GskGLIconCache *self,
+ GdkTexture *texture,
+ int *out_texture_id,
+ graphene_rect_t *out_texture_rect)
+{
+ IconData *icon_data = g_hash_table_lookup (self->icons, texture);
+
+ if (icon_data)
+ {
+ icon_data->frame_age = 0;
+ if (!icon_data->used)
+ {
+ const int w = icon_data->texture_rect.size.width * ATLAS_SIZE;
+ const int h = icon_data->texture_rect.size.height * ATLAS_SIZE;
+
+ gsk_gl_texture_atlas_mark_used (icon_data->atlas, w, h);
+ icon_data->used = TRUE;
+ }
+
+ *out_texture_id = icon_data->atlas->image.texture_id;
+ *out_texture_rect = icon_data->texture_rect;
+ return;
+ }
+
+ /* texture not on any atlas yet. Find a suitable one. */
+ {
+ const int twidth = gdk_texture_get_width (texture);
+ const int theight = gdk_texture_get_height (texture);
+ int packed_x, packed_y;
+ GskGLTextureAtlas *atlas = NULL;
+ guint i, p;
+ GskImageRegion region;
+ cairo_surface_t *surface;
+
+ g_assert (twidth < ATLAS_SIZE);
+ g_assert (theight < ATLAS_SIZE);
+
+ for (i = 0, p = self->atlases->len; i < p; i ++)
+ {
+ atlas = g_ptr_array_index (self->atlases, i);
+
+ if (gsk_gl_texture_atlas_pack (atlas, twidth, theight, &packed_x, &packed_y))
+ break;
+
+ atlas = NULL;
+ }
+
+ if (!atlas)
+ {
+ /* No atlas has enough space, so create a new one... */
+ atlas = g_malloc (sizeof (GskGLTextureAtlas));
+ gsk_gl_texture_atlas_init (atlas, ATLAS_SIZE, ATLAS_SIZE);
+ gsk_gl_image_create (&atlas->image, self->gl_driver, atlas->width, atlas->height);
+ /* Pack it onto that one, which surely has enought space... */
+ gsk_gl_texture_atlas_pack (atlas, twidth, theight, &packed_x, &packed_y);
+
+ g_ptr_array_add (self->atlases, atlas);
+ }
+
+ icon_data = g_new0 (IconData, 1);
+ icon_data->atlas = atlas;
+ icon_data->frame_age = 0;
+ icon_data->used = TRUE;
+ graphene_rect_init (&icon_data->texture_rect,
+ (float)packed_x / ATLAS_SIZE,
+ (float)packed_y / ATLAS_SIZE,
+ (float)twidth / ATLAS_SIZE,
+ (float)theight / ATLAS_SIZE);
+
+ g_hash_table_insert (self->icons, texture, icon_data);
+
+ /* actually upload the texture */
+ surface = gdk_texture_download_surface (texture);
+ region.x = packed_x;
+ region.y = packed_y;
+ region.width = twidth;
+ region.height = theight;
+ region.data = cairo_image_surface_get_data (surface);
+
+ gsk_gl_image_upload_region (&atlas->image, self->gl_driver, &region);
+
+ *out_texture_id = atlas->image.texture_id;
+ *out_texture_rect = icon_data->texture_rect;
+
+ cairo_surface_destroy (surface);
+
+#if 0
+ /* Some obvious debugging */
+ static int k;
+ gsk_gl_image_write_to_png (&atlas->image, self->gl_driver,
+ g_strdup_printf ("icon%d.png", k ++));
+#endif
+ }
+}
diff --git a/gsk/gl/gskgliconcacheprivate.h b/gsk/gl/gskgliconcacheprivate.h
new file mode 100644
index 0000000000..883a274ded
--- /dev/null
+++ b/gsk/gl/gskgliconcacheprivate.h
@@ -0,0 +1,31 @@
+#ifndef __GSK_GL_ICON_CACHE_PRIVATE_H__
+#define __GSK_GL_ICON_CACHE_PRIVATE_H__
+
+#include "gskgldriverprivate.h"
+#include "gskglimageprivate.h"
+#include "gskrendererprivate.h"
+#include "gskgltextureatlasprivate.h"
+#include <pango/pango.h>
+#include <gdk/gdk.h>
+
+typedef struct
+{
+ GskGLDriver *gl_driver;
+ GskRenderer *renderer;
+
+ GPtrArray *atlases;
+ GHashTable *icons; /* GdkTexture -> IconData */
+
+} GskGLIconCache;
+
+void gsk_gl_icon_cache_init (GskGLIconCache *self,
+ GskRenderer *renderer,
+ GskGLDriver *gl_driver);
+void gsk_gl_icon_cache_free (GskGLIconCache *self);
+void gsk_gl_icon_cache_begin_frame (GskGLIconCache *self);
+void gsk_gl_icon_cache_lookup_or_add (GskGLIconCache *self,
+ GdkTexture *texture,
+ int *out_texture_id,
+ graphene_rect_t *out_texture_rect);
+
+#endif
diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c
index fbc2c0a4e2..4265005e97 100644
--- a/gsk/gl/gskglrenderer.c
+++ b/gsk/gl/gskglrenderer.c
@@ -11,6 +11,7 @@
#include "gsktransformprivate.h"
#include "gskshaderbuilderprivate.h"
#include "gskglglyphcacheprivate.h"
+#include "gskgliconcacheprivate.h"
#include "gskglrenderopsprivate.h"
#include "gskcairoblurprivate.h"
#include "gskglshadowcacheprivate.h"
@@ -335,6 +336,7 @@ struct _GskGLRenderer
GArray *render_ops;
GskGLGlyphCache glyph_cache;
+ GskGLIconCache icon_cache;
GskGLShadowCache shadow_cache;
#ifdef G_ENABLE_DEBUG
@@ -823,27 +825,45 @@ render_texture_node (GskGLRenderer *self,
}
else
{
- int gl_min_filter = GL_NEAREST, gl_mag_filter = GL_NEAREST;
int texture_id;
+ float tx = 0, ty = 0, tx2 = 1, ty2 = 1;
+
+ if (texture->width <= 64 &&
+ texture->height <= 64)
+ {
+ graphene_rect_t trect;
+
+ gsk_gl_icon_cache_lookup_or_add (&self->icon_cache,
+ texture,
+ &texture_id,
+ &trect);
+ tx = trect.origin.x;
+ ty = trect.origin.y;
+ tx2 = tx + trect.size.width;
+ ty2 = ty + trect.size.height;
+ }
+ else
+ {
+ int gl_min_filter = GL_NEAREST, gl_mag_filter = GL_NEAREST;
+ get_gl_scaling_filters (node, &gl_min_filter, &gl_mag_filter);
- get_gl_scaling_filters (node, &gl_min_filter, &gl_mag_filter);
+ texture_id = gsk_gl_driver_get_texture_for_texture (self->gl_driver,
+ texture,
+ gl_min_filter,
+ gl_mag_filter);
+ }
- texture_id = gsk_gl_driver_get_texture_for_texture (self->gl_driver,
- texture,
- gl_min_filter,
- gl_mag_filter);
ops_set_program (builder, &self->blit_program);
ops_set_texture (builder, texture_id);
-
ops_draw (builder, (GskQuadVertex[GL_N_VERTICES]) {
- { { min_x, min_y }, { 0, 0 }, },
- { { min_x, max_y }, { 0, 1 }, },
- { { max_x, min_y }, { 1, 0 }, },
+ { { min_x, min_y }, { tx, ty }, },
+ { { min_x, max_y }, { tx, ty2 }, },
+ { { max_x, min_y }, { tx2, ty }, },
- { { max_x, max_y }, { 1, 1 }, },
- { { min_x, max_y }, { 0, 1 }, },
- { { max_x, min_y }, { 1, 0 }, },
+ { { max_x, max_y }, { tx2, ty2 }, },
+ { { min_x, max_y }, { tx, ty2 }, },
+ { { max_x, min_y }, { tx2, ty }, },
});
}
}
@@ -2482,6 +2502,7 @@ gsk_gl_renderer_realize (GskRenderer *renderer,
return FALSE;
gsk_gl_glyph_cache_init (&self->glyph_cache, renderer, self->gl_driver);
+ gsk_gl_icon_cache_init (&self->icon_cache, renderer, self->gl_driver);
gsk_gl_shadow_cache_init (&self->shadow_cache);
return TRUE;
@@ -2507,6 +2528,7 @@ gsk_gl_renderer_unrealize (GskRenderer *renderer)
glDeleteProgram (self->programs[i].id);
gsk_gl_glyph_cache_free (&self->glyph_cache);
+ gsk_gl_icon_cache_free (&self->icon_cache);
gsk_gl_shadow_cache_free (&self->shadow_cache, self->gl_driver);
g_clear_object (&self->gl_profiler);
@@ -3079,6 +3101,7 @@ gsk_gl_renderer_do_render (GskRenderer *renderer,
graphene_matrix_scale (&projection, 1, -1, 1);
gsk_gl_glyph_cache_begin_frame (&self->glyph_cache);
+ gsk_gl_icon_cache_begin_frame (&self->icon_cache);
gsk_gl_shadow_cache_begin_frame (&self->shadow_cache, self->gl_driver);
ops_set_projection (&self->op_builder, &projection);
diff --git a/gsk/meson.build b/gsk/meson.build
index af99a928e4..5c94f9303f 100644
--- a/gsk/meson.build
+++ b/gsk/meson.build
@@ -46,6 +46,7 @@ gsk_private_sources = files([
'gl/gskglshadowcache.c',
'gl/gskglnodesample.c',
'gl/gskgltextureatlas.c',
+ 'gl/gskgliconcache.c',
'gl/stb_rect_pack.c',
])