mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-11-15 05:00:09 +00:00
e9ba7eda47
Otherwise, we spread the cache over more atlases than necessary, increasing the amount of texture changes in each frame.
268 lines
8.3 KiB
C
268 lines
8.3 KiB
C
#include "gskgliconcacheprivate.h"
|
|
#include "gskgltextureatlasprivate.h"
|
|
#include "gdk/gdktextureprivate.h"
|
|
#include "gdk/gdkglcontextprivate.h"
|
|
|
|
#include <epoxy/gl.h>
|
|
|
|
#define MAX_FRAME_AGE 60
|
|
|
|
typedef struct
|
|
{
|
|
graphene_rect_t texture_rect;
|
|
GskGLTextureAtlas *atlas;
|
|
GdkTexture *source_texture;
|
|
guint accessed : 1;
|
|
guint used : 1;
|
|
} IconData;
|
|
|
|
static void
|
|
icon_data_free (gpointer p)
|
|
{
|
|
g_object_unref (((IconData *)p)->source_texture);
|
|
g_free (p);
|
|
}
|
|
|
|
GskGLIconCache *
|
|
gsk_gl_icon_cache_new (GdkDisplay *display,
|
|
GskGLTextureAtlases *atlases)
|
|
{
|
|
GskGLIconCache *self;
|
|
|
|
self = g_new0 (GskGLIconCache, 1);
|
|
|
|
self->display = display;
|
|
self->icons = g_hash_table_new_full (NULL, NULL, NULL, icon_data_free);
|
|
self->atlases = gsk_gl_texture_atlases_ref (atlases);
|
|
self->ref_count = 1;
|
|
|
|
return self;
|
|
}
|
|
|
|
GskGLIconCache *
|
|
gsk_gl_icon_cache_ref (GskGLIconCache *self)
|
|
{
|
|
self->ref_count++;
|
|
|
|
return self;
|
|
}
|
|
|
|
void
|
|
gsk_gl_icon_cache_unref (GskGLIconCache *self)
|
|
{
|
|
g_assert (self->ref_count > 0);
|
|
|
|
if (self->ref_count == 1)
|
|
{
|
|
gsk_gl_texture_atlases_unref (self->atlases);
|
|
g_hash_table_unref (self->icons);
|
|
g_free (self);
|
|
return;
|
|
}
|
|
|
|
self->ref_count--;
|
|
}
|
|
|
|
void
|
|
gsk_gl_icon_cache_begin_frame (GskGLIconCache *self,
|
|
GPtrArray *removed_atlases)
|
|
{
|
|
GHashTableIter iter;
|
|
GdkTexture *texture;
|
|
IconData *icon_data;
|
|
|
|
self->timestamp++;
|
|
|
|
/* Drop icons on removed atlases */
|
|
if (removed_atlases->len > 0)
|
|
{
|
|
guint dropped = 0;
|
|
|
|
g_hash_table_iter_init (&iter, self->icons);
|
|
while (g_hash_table_iter_next (&iter, (gpointer *)&texture, (gpointer *)&icon_data))
|
|
{
|
|
if (g_ptr_array_find (removed_atlases, icon_data->atlas, NULL))
|
|
{
|
|
g_hash_table_iter_remove (&iter);
|
|
dropped++;
|
|
}
|
|
}
|
|
|
|
GSK_NOTE(GLYPH_CACHE, if (dropped > 0) g_message ("Dropped %d icons", dropped));
|
|
}
|
|
|
|
if (self->timestamp % MAX_FRAME_AGE == 0)
|
|
{
|
|
g_hash_table_iter_init (&iter, self->icons);
|
|
while (g_hash_table_iter_next (&iter, (gpointer *)&texture, (gpointer *)&icon_data))
|
|
{
|
|
if (!icon_data->accessed)
|
|
{
|
|
if (icon_data->used)
|
|
{
|
|
const int w = icon_data->texture_rect.size.width * icon_data->atlas->width;
|
|
const int h = icon_data->texture_rect.size.height * icon_data->atlas->height;
|
|
gsk_gl_texture_atlas_mark_unused (icon_data->atlas, w + 2, h + 2);
|
|
icon_data->used = FALSE;
|
|
}
|
|
}
|
|
|
|
icon_data->accessed = FALSE;
|
|
}
|
|
|
|
GSK_NOTE(GLYPH_CACHE, g_message ("%d icons cached", g_hash_table_size (self->icons)));
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
if (!icon_data->used)
|
|
{
|
|
const int w = icon_data->texture_rect.size.width * icon_data->atlas->width;
|
|
const int h = icon_data->texture_rect.size.height * icon_data->atlas->height;
|
|
|
|
gsk_gl_texture_atlas_mark_used (icon_data->atlas, w + 2, h + 2);
|
|
icon_data->used = TRUE;
|
|
}
|
|
icon_data->accessed = TRUE;
|
|
|
|
*out_texture_id = icon_data->atlas->texture_id;
|
|
*out_texture_rect = icon_data->texture_rect;
|
|
return;
|
|
}
|
|
|
|
/* texture not on any atlas yet. Find a suitable one. */
|
|
{
|
|
const int width = gdk_texture_get_width (texture);
|
|
const int height = gdk_texture_get_height (texture);
|
|
GskGLTextureAtlas *atlas = NULL;
|
|
int packed_x = 0;
|
|
int packed_y = 0;
|
|
cairo_surface_t *surface;
|
|
unsigned char *surface_data;
|
|
|
|
gsk_gl_texture_atlases_pack (self->atlases, width + 2, height + 2, &atlas, &packed_x, &packed_y);
|
|
|
|
icon_data = g_new0 (IconData, 1);
|
|
icon_data->atlas = atlas;
|
|
icon_data->accessed = TRUE;
|
|
icon_data->used = TRUE;
|
|
icon_data->source_texture = g_object_ref (texture);
|
|
graphene_rect_init (&icon_data->texture_rect,
|
|
(float)(packed_x + 1) / atlas->width,
|
|
(float)(packed_y + 1) / atlas->height,
|
|
(float)width / atlas->width,
|
|
(float)height / atlas->height);
|
|
|
|
g_hash_table_insert (self->icons, texture, icon_data);
|
|
|
|
/* actually upload the texture */
|
|
surface = gdk_texture_download_surface (texture);
|
|
surface_data = cairo_image_surface_get_data (surface);
|
|
gdk_gl_context_push_debug_group_printf (gdk_gl_context_get_current (),
|
|
"Uploading texture");
|
|
glBindTexture (GL_TEXTURE_2D, atlas->texture_id);
|
|
|
|
glTexSubImage2D (GL_TEXTURE_2D, 0,
|
|
packed_x + 1, packed_y + 1,
|
|
width, height,
|
|
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
|
|
surface_data);
|
|
/* Padding top */
|
|
glTexSubImage2D (GL_TEXTURE_2D, 0,
|
|
packed_x + 1, packed_y,
|
|
width, 1,
|
|
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
|
|
surface_data);
|
|
/* Padding left */
|
|
glTexSubImage2D (GL_TEXTURE_2D, 0,
|
|
packed_x, packed_y + 1,
|
|
1, height,
|
|
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
|
|
surface_data);
|
|
/* Padding top left */
|
|
glTexSubImage2D (GL_TEXTURE_2D, 0,
|
|
packed_x, packed_y,
|
|
1, 1,
|
|
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
|
|
surface_data);
|
|
|
|
/* Padding right */
|
|
glPixelStorei (GL_UNPACK_ROW_LENGTH, width);
|
|
glPixelStorei (GL_UNPACK_SKIP_PIXELS, width - 1);
|
|
glTexSubImage2D (GL_TEXTURE_2D, 0,
|
|
packed_x + width + 1, packed_y + 1,
|
|
1, height,
|
|
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
|
|
surface_data);
|
|
/* Padding top right */
|
|
glTexSubImage2D (GL_TEXTURE_2D, 0,
|
|
packed_x + width + 1, packed_y,
|
|
1, 1,
|
|
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
|
|
surface_data);
|
|
/* Padding bottom */
|
|
glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0);
|
|
glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
|
|
glPixelStorei (GL_UNPACK_SKIP_ROWS, height - 1);
|
|
glTexSubImage2D (GL_TEXTURE_2D, 0,
|
|
packed_x + 1, packed_y + 1 + height,
|
|
width, 1,
|
|
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
|
|
surface_data);
|
|
/* Padding bottom left */
|
|
glTexSubImage2D (GL_TEXTURE_2D, 0,
|
|
packed_x, packed_y + 1 + height,
|
|
1, 1,
|
|
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
|
|
surface_data);
|
|
/* Padding bottom right */
|
|
glPixelStorei (GL_UNPACK_ROW_LENGTH, width);
|
|
glPixelStorei (GL_UNPACK_SKIP_PIXELS, width - 1);
|
|
glTexSubImage2D (GL_TEXTURE_2D, 0,
|
|
packed_x + 1 + width, packed_y + 1 + height,
|
|
1, 1,
|
|
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
|
|
surface_data);
|
|
|
|
/* Reset this */
|
|
glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0);
|
|
glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
|
|
glPixelStorei (GL_UNPACK_SKIP_ROWS, 0);
|
|
|
|
gdk_gl_context_pop_debug_group (gdk_gl_context_get_current ());
|
|
|
|
*out_texture_id = atlas->texture_id;
|
|
*out_texture_rect = icon_data->texture_rect;
|
|
|
|
cairo_surface_destroy (surface);
|
|
|
|
#if 0
|
|
{
|
|
static int k;
|
|
const int stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, atlas->width);
|
|
guchar *data = g_malloc (atlas->height * stride);
|
|
cairo_surface_t *s;
|
|
char *filename = g_strdup_printf ("atlas_%u_%d.png", atlas->texture_id, k++);
|
|
|
|
glBindTexture (GL_TEXTURE_2D, atlas->texture_id);
|
|
glGetTexImage (GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, data);
|
|
s = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32, atlas->width, atlas->height, stride);
|
|
cairo_surface_write_to_png (s, filename);
|
|
|
|
cairo_surface_destroy (s);
|
|
g_free (data);
|
|
g_free (filename);
|
|
}
|
|
#endif
|
|
}
|
|
}
|