1 #include "gskgliconcacheprivate.h"
2 #include "gskgltextureatlasprivate.h"
3 #include "gdk/gdktextureprivate.h"
4 #include "gdk/gdkmemorytextureprivate.h"
5 #include "gdk/gdkglcontextprivate.h"
6 
7 #include <epoxy/gl.h>
8 
9 #define MAX_FRAME_AGE 60
10 
11 static void
icon_data_free(gpointer p)12 icon_data_free (gpointer p)
13 {
14   g_object_unref (((IconData *)p)->source_texture);
15   g_free (p);
16 }
17 
18 GskGLIconCache *
gsk_gl_icon_cache_new(GdkDisplay * display,GskGLTextureAtlases * atlases)19 gsk_gl_icon_cache_new (GdkDisplay *display,
20                        GskGLTextureAtlases *atlases)
21 {
22   GskGLIconCache *self;
23 
24   self = g_new0 (GskGLIconCache, 1);
25 
26   self->display = display;
27   self->icons = g_hash_table_new_full (NULL, NULL, NULL, icon_data_free);
28   self->atlases = gsk_gl_texture_atlases_ref (atlases);
29   self->ref_count = 1;
30 
31   return self;
32 }
33 
34 GskGLIconCache *
gsk_gl_icon_cache_ref(GskGLIconCache * self)35 gsk_gl_icon_cache_ref (GskGLIconCache *self)
36 {
37   self->ref_count++;
38 
39   return self;
40 }
41 
42 void
gsk_gl_icon_cache_unref(GskGLIconCache * self)43 gsk_gl_icon_cache_unref (GskGLIconCache *self)
44 {
45   g_assert (self->ref_count > 0);
46 
47   if (self->ref_count == 1)
48     {
49       gsk_gl_texture_atlases_unref (self->atlases);
50       g_hash_table_unref (self->icons);
51       g_free (self);
52       return;
53     }
54 
55   self->ref_count--;
56 }
57 
58 void
gsk_gl_icon_cache_begin_frame(GskGLIconCache * self,GPtrArray * removed_atlases)59 gsk_gl_icon_cache_begin_frame (GskGLIconCache *self,
60                                GPtrArray      *removed_atlases)
61 {
62   GHashTableIter iter;
63   GdkTexture *texture;
64   IconData *icon_data;
65 
66   self->timestamp++;
67 
68   /* Drop icons on removed atlases */
69   if (removed_atlases->len > 0)
70     {
71       guint dropped = 0;
72 
73       g_hash_table_iter_init (&iter, self->icons);
74       while (g_hash_table_iter_next (&iter, (gpointer *)&texture, (gpointer *)&icon_data))
75         {
76           if (g_ptr_array_find (removed_atlases, icon_data->atlas, NULL))
77             {
78               g_hash_table_iter_remove (&iter);
79               dropped++;
80             }
81         }
82 
83       GSK_NOTE(GLYPH_CACHE, if (dropped > 0) g_message ("Dropped %d icons", dropped));
84     }
85 
86   if (self->timestamp % MAX_FRAME_AGE == 0)
87     {
88       g_hash_table_iter_init (&iter, self->icons);
89       while (g_hash_table_iter_next (&iter, (gpointer *)&texture, (gpointer *)&icon_data))
90         {
91           if (!icon_data->accessed)
92             {
93               if (icon_data->used)
94                 {
95                   const int width = icon_data->source_texture->width;
96                   const int height = icon_data->source_texture->height;
97                   gsk_gl_texture_atlas_mark_unused (icon_data->atlas, width + 2, height + 2);
98                   icon_data->used = FALSE;
99                 }
100             }
101 
102           icon_data->accessed = FALSE;
103         }
104 
105       GSK_NOTE(GLYPH_CACHE, g_message ("%d icons cached", g_hash_table_size (self->icons)));
106     }
107 }
108 
109 void
gsk_gl_icon_cache_lookup_or_add(GskGLIconCache * self,GdkTexture * texture,const IconData ** out_icon_data)110 gsk_gl_icon_cache_lookup_or_add (GskGLIconCache  *self,
111                                  GdkTexture      *texture,
112                                  const IconData **out_icon_data)
113 {
114   IconData *icon_data = g_hash_table_lookup (self->icons, texture);
115 
116   if (icon_data)
117     {
118       if (!icon_data->used)
119         {
120           gsk_gl_texture_atlas_mark_used (icon_data->atlas, texture->width + 2, texture->height + 2);
121           icon_data->used = TRUE;
122         }
123       icon_data->accessed = TRUE;
124 
125       *out_icon_data = icon_data;
126       return;
127     }
128 
129   /* texture not on any atlas yet. Find a suitable one. */
130   {
131     const int width = texture->width;
132     const int height = texture->height;
133     GskGLTextureAtlas *atlas = NULL;
134     int packed_x = 0;
135     int packed_y = 0;
136     cairo_surface_t *surface;
137     unsigned char *surface_data;
138     unsigned char *pixel_data;
139     guchar *free_data = NULL;
140     guint gl_format;
141     guint gl_type;
142 
143     gsk_gl_texture_atlases_pack (self->atlases, width + 2, height + 2, &atlas, &packed_x, &packed_y);
144 
145     icon_data = g_new0 (IconData, 1);
146     icon_data->atlas = atlas;
147     icon_data->accessed = TRUE;
148     icon_data->used = TRUE;
149     icon_data->texture_id = atlas->texture_id;
150     icon_data->source_texture = g_object_ref (texture);
151     icon_data->x = (float)(packed_x + 1) / atlas->width;
152     icon_data->y = (float)(packed_y + 1) / atlas->width;
153     icon_data->x2 = icon_data->x + (float)width / atlas->width;
154     icon_data->y2 = icon_data->y + (float)height / atlas->height;
155 
156     g_hash_table_insert (self->icons, texture, icon_data);
157 
158     /* actually upload the texture */
159     surface = gdk_texture_download_surface (texture);
160     surface_data = cairo_image_surface_get_data (surface);
161     gdk_gl_context_push_debug_group_printf (gdk_gl_context_get_current (),
162                                             "Uploading texture");
163 
164     if (gdk_gl_context_get_use_es (gdk_gl_context_get_current ()))
165       {
166         pixel_data = free_data = g_malloc (width * height * 4);
167         gdk_memory_convert (pixel_data, width * 4,
168                             GDK_MEMORY_R8G8B8A8_PREMULTIPLIED,
169                             surface_data, cairo_image_surface_get_stride (surface),
170                             GDK_MEMORY_DEFAULT, width, height);
171         gl_format = GL_RGBA;
172         gl_type = GL_UNSIGNED_BYTE;
173       }
174     else
175       {
176         pixel_data = surface_data;
177         gl_format = GL_BGRA;
178         gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
179       }
180 
181     glBindTexture (GL_TEXTURE_2D, atlas->texture_id);
182 
183     glTexSubImage2D (GL_TEXTURE_2D, 0,
184                      packed_x + 1, packed_y + 1,
185                      width, height,
186                      gl_format, gl_type,
187                      pixel_data);
188     /* Padding top */
189     glTexSubImage2D (GL_TEXTURE_2D, 0,
190                      packed_x + 1, packed_y,
191                      width, 1,
192                      gl_format, gl_type,
193                      pixel_data);
194     /* Padding left */
195     glTexSubImage2D (GL_TEXTURE_2D, 0,
196                      packed_x, packed_y + 1,
197                      1, height,
198                      gl_format, gl_type,
199                      pixel_data);
200     /* Padding top left */
201     glTexSubImage2D (GL_TEXTURE_2D, 0,
202                      packed_x, packed_y,
203                      1, 1,
204                      gl_format, gl_type,
205                      pixel_data);
206 
207     /* Padding right */
208     glPixelStorei (GL_UNPACK_ROW_LENGTH, width);
209     glPixelStorei (GL_UNPACK_SKIP_PIXELS, width - 1);
210     glTexSubImage2D (GL_TEXTURE_2D, 0,
211                      packed_x + width + 1, packed_y + 1,
212                      1, height,
213                      gl_format, gl_type,
214                      pixel_data);
215     /* Padding top right */
216     glTexSubImage2D (GL_TEXTURE_2D, 0,
217                      packed_x + width + 1, packed_y,
218                      1, 1,
219                      gl_format, gl_type,
220                      pixel_data);
221     /* Padding bottom */
222     glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0);
223     glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
224     glPixelStorei (GL_UNPACK_SKIP_ROWS, height - 1);
225     glTexSubImage2D (GL_TEXTURE_2D, 0,
226                      packed_x + 1, packed_y + 1 + height,
227                      width, 1,
228                      gl_format, gl_type,
229                      pixel_data);
230     /* Padding bottom left */
231     glTexSubImage2D (GL_TEXTURE_2D, 0,
232                      packed_x, packed_y + 1 + height,
233                      1, 1,
234                      gl_format, gl_type,
235                      pixel_data);
236     /* Padding bottom right */
237     glPixelStorei (GL_UNPACK_ROW_LENGTH, width);
238     glPixelStorei (GL_UNPACK_SKIP_PIXELS, width - 1);
239     glTexSubImage2D (GL_TEXTURE_2D, 0,
240                      packed_x + 1 + width, packed_y + 1 + height,
241                      1, 1,
242                      gl_format, gl_type,
243                      pixel_data);
244     /* Reset this */
245     glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0);
246     glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
247     glPixelStorei (GL_UNPACK_SKIP_ROWS, 0);
248 
249     gdk_gl_context_pop_debug_group (gdk_gl_context_get_current ());
250 
251     *out_icon_data = icon_data;
252 
253     cairo_surface_destroy (surface);
254     g_free (free_data);
255 
256 #if 0
257     {
258       static int k;
259       const int stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, atlas->width);
260       guchar *data = g_malloc (atlas->height * stride);
261       cairo_surface_t *s;
262       char *filename = g_strdup_printf ("atlas_%u_%d.png", atlas->texture_id, k++);
263 
264       glBindTexture (GL_TEXTURE_2D, atlas->texture_id);
265       glGetTexImage (GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_BYTE, data);
266       s = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32, atlas->width, atlas->height, stride);
267       cairo_surface_write_to_png (s, filename);
268 
269       cairo_surface_destroy (s);
270       g_free (data);
271       g_free (filename);
272     }
273 #endif
274   }
275 }
276