mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-12-27 06:00:22 +00:00
gl renderer: Add & use icon cache
Upload small icons all to the same texture atlas.
This commit is contained in:
parent
e2ffad7b07
commit
cf4ff56ca5
194
gsk/gl/gskgliconcache.c
Normal file
194
gsk/gl/gskgliconcache.c
Normal file
@ -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, ®ion);
|
||||
|
||||
*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
|
||||
}
|
||||
}
|
31
gsk/gl/gskgliconcacheprivate.h
Normal file
31
gsk/gl/gskgliconcacheprivate.h
Normal file
@ -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
|
@ -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;
|
||||
|
||||
get_gl_scaling_filters (node, &gl_min_filter, &gl_mag_filter);
|
||||
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);
|
||||
|
||||
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 }, },
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -2476,6 +2496,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;
|
||||
@ -2501,6 +2522,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);
|
||||
@ -3067,6 +3089,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);
|
||||
|
@ -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',
|
||||
])
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user