1
2 #include "config.h"
3 #include "gskgltextureatlasprivate.h"
4 #include "gskdebugprivate.h"
5 #include "gdkglcontextprivate.h"
6 #include <epoxy/gl.h>
7
8 #define ATLAS_SIZE (512)
9 #define MAX_OLD_RATIO 0.5
10
11 static void
free_atlas(gpointer v)12 free_atlas (gpointer v)
13 {
14 GskGLTextureAtlas *atlas = v;
15
16 gsk_gl_texture_atlas_free (atlas);
17
18 g_free (atlas);
19 }
20
21 GskGLTextureAtlases *
gsk_gl_texture_atlases_new(void)22 gsk_gl_texture_atlases_new (void)
23 {
24 GskGLTextureAtlases *self;
25
26 self = g_new (GskGLTextureAtlases, 1);
27 self->atlases = g_ptr_array_new_with_free_func (free_atlas);
28
29 self->ref_count = 1;
30
31 return self;
32 }
33
34 GskGLTextureAtlases *
gsk_gl_texture_atlases_ref(GskGLTextureAtlases * self)35 gsk_gl_texture_atlases_ref (GskGLTextureAtlases *self)
36 {
37 self->ref_count++;
38
39 return self;
40 }
41
42 void
gsk_gl_texture_atlases_unref(GskGLTextureAtlases * self)43 gsk_gl_texture_atlases_unref (GskGLTextureAtlases *self)
44 {
45 g_assert (self->ref_count > 0);
46
47 if (self->ref_count == 1)
48 {
49 g_ptr_array_unref (self->atlases);
50 g_free (self);
51 return;
52 }
53
54 self->ref_count--;
55 }
56
57 #if 0
58 static void
59 write_atlas_to_png (GskGLTextureAtlas *atlas,
60 const char *filename)
61 {
62 int stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, atlas->width);
63 guchar *data = g_malloc (atlas->height * stride);
64 cairo_surface_t *s;
65
66 glBindTexture (GL_TEXTURE_2D, atlas->texture_id);
67 glGetTexImage (GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, data);
68 s = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32, atlas->width, atlas->height, stride);
69 cairo_surface_write_to_png (s, filename);
70
71 cairo_surface_destroy (s);
72 g_free (data);
73 }
74 #endif
75
76 void
gsk_gl_texture_atlases_begin_frame(GskGLTextureAtlases * self,GPtrArray * removed)77 gsk_gl_texture_atlases_begin_frame (GskGLTextureAtlases *self,
78 GPtrArray *removed)
79 {
80 int i;
81
82 for (i = self->atlases->len - 1; i >= 0; i--)
83 {
84 GskGLTextureAtlas *atlas = g_ptr_array_index (self->atlases, i);
85
86 if (gsk_gl_texture_atlas_get_unused_ratio (atlas) > MAX_OLD_RATIO)
87 {
88 GSK_NOTE(GLYPH_CACHE,
89 g_message ("Dropping atlas %d (%g.2%% old)", i,
90 100.0 * gsk_gl_texture_atlas_get_unused_ratio (atlas)));
91
92 if (atlas->texture_id != 0)
93 {
94 glDeleteTextures (1, &atlas->texture_id);
95 atlas->texture_id = 0;
96 }
97
98 g_ptr_array_add (removed, atlas);
99 g_ptr_array_remove_index (self->atlases, i);
100 }
101 }
102
103 GSK_NOTE(GLYPH_CACHE, {
104 static guint timestamp;
105 if (timestamp++ % 60 == 0)
106 g_message ("%d atlases", self->atlases->len);
107 });
108
109
110 #if 0
111 {
112 static guint timestamp;
113
114 timestamp++;
115 if (timestamp % 10 == 0)
116 for (i = 0; i < self->atlases->len; i++)
117 {
118 GskGLTextureAtlas *atlas = g_ptr_array_index (self->atlases, i);
119
120 if (atlas->texture_id)
121 {
122 char *filename;
123
124 filename = g_strdup_printf ("textureatlas%d-%u.png", i, timestamp);
125 write_atlas_to_png (atlas, filename);
126 g_free (filename);
127 }
128 }
129 }
130 #endif
131 }
132
133 gboolean
gsk_gl_texture_atlases_pack(GskGLTextureAtlases * self,int width,int height,GskGLTextureAtlas ** atlas_out,int * out_x,int * out_y)134 gsk_gl_texture_atlases_pack (GskGLTextureAtlases *self,
135 int width,
136 int height,
137 GskGLTextureAtlas **atlas_out,
138 int *out_x,
139 int *out_y)
140 {
141 GskGLTextureAtlas *atlas;
142 int x, y;
143 int i;
144
145 g_assert (width < ATLAS_SIZE);
146 g_assert (height < ATLAS_SIZE);
147
148 atlas = NULL;
149
150 for (i = 0; i < self->atlases->len; i++)
151 {
152 atlas = g_ptr_array_index (self->atlases, i);
153
154 if (gsk_gl_texture_atlas_pack (atlas, width, height, &x, &y))
155 break;
156
157 atlas = NULL;
158 }
159
160 if (atlas == NULL)
161 {
162 /* No atlas has enough space, so create a new one... */
163 atlas = g_malloc (sizeof (GskGLTextureAtlas));
164 gsk_gl_texture_atlas_init (atlas, ATLAS_SIZE, ATLAS_SIZE);
165 gsk_gl_texture_atlas_realize (atlas);
166 g_ptr_array_add (self->atlases, atlas);
167
168 /* Pack it onto that one, which surely has enough space... */
169 if (!gsk_gl_texture_atlas_pack (atlas, width, height, &x, &y))
170 g_assert_not_reached ();
171
172 GSK_NOTE(GLYPH_CACHE, g_message ("adding new atlas"));
173 }
174
175 *atlas_out = atlas;
176 *out_x = x;
177 *out_y = y;
178
179 return TRUE;
180 }
181
182 void
gsk_gl_texture_atlas_init(GskGLTextureAtlas * self,int width,int height)183 gsk_gl_texture_atlas_init (GskGLTextureAtlas *self,
184 int width,
185 int height)
186 {
187 memset (self, 0, sizeof (*self));
188
189 self->texture_id = 0;
190 self->width = width;
191 self->height = height;
192
193 /* TODO: We might want to change the strategy about the amount of
194 * nodes here? stb_rect_pack.h says with is optimal. */
195 self->nodes = g_malloc0 (sizeof (struct stbrp_node) * width);
196 stbrp_init_target (&self->context,
197 width, height,
198 self->nodes,
199 width);
200
201 gsk_gl_texture_atlas_realize (self);
202 }
203
204 void
gsk_gl_texture_atlas_free(GskGLTextureAtlas * self)205 gsk_gl_texture_atlas_free (GskGLTextureAtlas *self)
206 {
207 if (self->texture_id != 0)
208 {
209 glDeleteTextures (1, &self->texture_id);
210 self->texture_id = 0;
211 }
212
213 g_clear_pointer (&self->nodes, g_free);
214 }
215
216 void
gsk_gl_texture_atlas_mark_unused(GskGLTextureAtlas * self,int width,int height)217 gsk_gl_texture_atlas_mark_unused (GskGLTextureAtlas *self,
218 int width,
219 int height)
220 {
221 self->unused_pixels += (width * height);
222 }
223
224
225 void
gsk_gl_texture_atlas_mark_used(GskGLTextureAtlas * self,int width,int height)226 gsk_gl_texture_atlas_mark_used (GskGLTextureAtlas *self,
227 int width,
228 int height)
229 {
230 self->unused_pixels -= (width * height);
231
232 g_assert (self->unused_pixels >= 0);
233 }
234
235 gboolean
gsk_gl_texture_atlas_pack(GskGLTextureAtlas * self,int width,int height,int * out_x,int * out_y)236 gsk_gl_texture_atlas_pack (GskGLTextureAtlas *self,
237 int width,
238 int height,
239 int *out_x,
240 int *out_y)
241 {
242 stbrp_rect rect;
243
244 g_assert (out_x);
245 g_assert (out_y);
246
247 rect.w = width;
248 rect.h = height;
249
250 stbrp_pack_rects (&self->context, &rect, 1);
251
252 if (rect.was_packed)
253 {
254 *out_x = rect.x;
255 *out_y = rect.y;
256 }
257
258 return rect.was_packed;
259 }
260
261 double
gsk_gl_texture_atlas_get_unused_ratio(const GskGLTextureAtlas * self)262 gsk_gl_texture_atlas_get_unused_ratio (const GskGLTextureAtlas *self)
263 {
264 if (self->unused_pixels > 0)
265 return (double)(self->unused_pixels) / (double)(self->width * self->height);
266
267 return 0.0;
268 }
269
270 /* Not using gdk_gl_driver_create_texture here, since we want
271 * this texture to survive the driver and stay around until
272 * the display gets closed.
273 */
274 static guint
create_shared_texture(int width,int height)275 create_shared_texture (int width,
276 int height)
277 {
278 guint texture_id;
279
280 glGenTextures (1, &texture_id);
281 glBindTexture (GL_TEXTURE_2D, texture_id);
282
283 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
284 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
285
286 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
287 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
288
289 if (gdk_gl_context_get_use_es (gdk_gl_context_get_current ()))
290 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
291 else
292 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
293
294 glBindTexture (GL_TEXTURE_2D, 0);
295
296 return texture_id;
297 }
298
299 void
gsk_gl_texture_atlas_realize(GskGLTextureAtlas * atlas)300 gsk_gl_texture_atlas_realize (GskGLTextureAtlas *atlas)
301 {
302 if (atlas->texture_id)
303 return;
304
305 atlas->texture_id = create_shared_texture (atlas->width, atlas->height);
306 gdk_gl_context_label_object_printf (gdk_gl_context_get_current (),
307 GL_TEXTURE, atlas->texture_id,
308 "Texture atlas %d", atlas->texture_id);
309 }
310