Merge branch 'wip/chergert/gsk-gl-texture-library' into 'main'

gsk/gl: texture libraries, shader creation

See merge request GNOME/gtk!4583
This commit is contained in:
Matthias Clasen 2022-03-18 23:46:17 +00:00
commit dcc7cf7114
10 changed files with 532 additions and 327 deletions

View File

@ -512,7 +512,7 @@ load_file (const char *demoname,
info_buffer = gtk_text_buffer_new (NULL);
gtk_text_buffer_create_tag (info_buffer, "title",
"font", "Sans 18",
"size", 18 * 1024,
"pixels-below-lines", 10,
NULL);

View File

@ -282,7 +282,7 @@ snapshot_uniforms (GskGLUniformState *state,
{
const GskGLUniformMapping *mapping = &program->mappings[i];
if (!mapping->info.initial && mapping->location > -1)
if (!mapping->info.initial && mapping->info.format && mapping->location > -1)
{
uniform[count].location = mapping->location;
uniform[count].info = mapping->info;

View File

@ -44,9 +44,6 @@
#include <gdk/gdkprofilerprivate.h>
#include <gdk/gdktextureprivate.h>
#define ATLAS_SIZE 512
#define MAX_OLD_RATIO 0.5
G_DEFINE_TYPE (GskGLDriver, gsk_gl_driver, G_TYPE_OBJECT)
static guint
@ -156,50 +153,6 @@ gsk_gl_driver_collect_unused_textures (GskGLDriver *self,
return collected;
}
static void
gsk_gl_texture_atlas_free (GskGLTextureAtlas *atlas)
{
if (atlas->texture_id != 0)
{
glDeleteTextures (1, &atlas->texture_id);
atlas->texture_id = 0;
}
g_clear_pointer (&atlas->nodes, g_free);
g_slice_free (GskGLTextureAtlas, atlas);
}
GskGLTextureAtlas *
gsk_gl_driver_create_atlas (GskGLDriver *self)
{
GskGLTextureAtlas *atlas;
g_return_val_if_fail (GSK_IS_GL_DRIVER (self), NULL);
atlas = g_slice_new0 (GskGLTextureAtlas);
atlas->width = ATLAS_SIZE;
atlas->height = ATLAS_SIZE;
/* TODO: We might want to change the strategy about the amount of
* nodes here? stb_rect_pack.h says width is optimal. */
atlas->nodes = g_malloc0_n (atlas->width, sizeof (struct stbrp_node));
stbrp_init_target (&atlas->context, atlas->width, atlas->height, atlas->nodes, atlas->width);
atlas->texture_id = gsk_gl_command_queue_create_texture (self->command_queue,
atlas->width,
atlas->height,
GL_RGBA8,
GL_LINEAR,
GL_LINEAR);
gdk_gl_context_label_object_printf (gdk_gl_context_get_current (),
GL_TEXTURE, atlas->texture_id,
"Texture atlas %d",
atlas->texture_id);
g_ptr_array_add (self->atlases, atlas);
return atlas;
}
static void
remove_program (gpointer data)
{
@ -226,6 +179,29 @@ gsk_gl_driver_shader_weak_cb (gpointer data,
g_hash_table_remove (self->shader_cache, where_object_was);
}
G_GNUC_NULL_TERMINATED static inline GBytes *
join_sources (GBytes *first_bytes,
...)
{
GByteArray *byte_array = g_byte_array_new ();
GBytes *bytes = first_bytes;
va_list args;
va_start (args, first_bytes);
while (bytes != NULL)
{
gsize len;
const guint8 *data = g_bytes_get_data (bytes, &len);
if (len > 0)
g_byte_array_append (byte_array, data, len);
g_bytes_unref (bytes);
bytes = va_arg (args, GBytes *);
}
va_end (args);
return g_byte_array_free_to_bytes (byte_array);
}
static void
gsk_gl_driver_dispose (GObject *object)
{
@ -235,6 +211,10 @@ gsk_gl_driver_dispose (GObject *object)
g_assert (self->in_frame == FALSE);
#define GSK_GL_NO_UNIFORMS
#define GSK_GL_SHADER_RESOURCE(name)
#define GSK_GL_SHADER_STRING(str)
#define GSK_GL_SHADER_SINGLE(name)
#define GSK_GL_SHADER_JOINED(kind, ...)
#define GSK_GL_ADD_UNIFORM(pos, KEY, name)
#define GSK_GL_DEFINE_PROGRAM(name, resource, uniforms) \
GSK_GL_DELETE_PROGRAM(name); \
@ -248,6 +228,10 @@ gsk_gl_driver_dispose (GObject *object)
} G_STMT_END;
# include "gskglprograms.defs"
#undef GSK_GL_NO_UNIFORMS
#undef GSK_GL_SHADER_RESOURCE
#undef GSK_GL_SHADER_STRING
#undef GSK_GL_SHADER_SINGLE
#undef GSK_GL_SHADER_JOINED
#undef GSK_GL_ADD_UNIFORM
#undef GSK_GL_DEFINE_PROGRAM
@ -289,11 +273,10 @@ gsk_gl_driver_dispose (GObject *object)
g_assert (!self->texture_id_to_key || g_hash_table_size (self->texture_id_to_key) == 0);
g_assert (!self->key_to_texture_id|| g_hash_table_size (self->key_to_texture_id) == 0);
g_clear_object (&self->glyphs);
g_clear_object (&self->icons);
g_clear_object (&self->shadows);
g_clear_object (&self->glyphs_library);
g_clear_object (&self->icons_library);
g_clear_object (&self->shadows_library);
g_clear_pointer (&self->atlases, g_ptr_array_unref);
g_clear_pointer (&self->autorelease_framebuffers, g_array_unref);
g_clear_pointer (&self->key_to_texture_id, g_hash_table_unref);
g_clear_pointer (&self->textures, g_hash_table_unref);
@ -330,7 +313,6 @@ gsk_gl_driver_init (GskGLDriver *self)
self->shader_cache = g_hash_table_new_full (NULL, NULL, NULL, remove_program);
self->texture_pool = g_array_new (FALSE, FALSE, sizeof (guint));
self->render_targets = g_ptr_array_new ();
self->atlases = g_ptr_array_new_with_free_func ((GDestroyNotify)gsk_gl_texture_atlas_free);
}
static gboolean
@ -348,14 +330,14 @@ gsk_gl_driver_load_programs (GskGLDriver *self,
/* Setup preambles that are shared by all shaders */
gsk_gl_compiler_set_preamble_from_resource (compiler,
GSK_GL_COMPILER_ALL,
"/org/gtk/libgsk/gl/preamble.glsl");
GSK_GL_COMPILER_ALL,
"/org/gtk/libgsk/gl/preamble.glsl");
gsk_gl_compiler_set_preamble_from_resource (compiler,
GSK_GL_COMPILER_VERTEX,
"/org/gtk/libgsk/gl/preamble.vs.glsl");
GSK_GL_COMPILER_VERTEX,
"/org/gtk/libgsk/gl/preamble.vs.glsl");
gsk_gl_compiler_set_preamble_from_resource (compiler,
GSK_GL_COMPILER_FRAGMENT,
"/org/gtk/libgsk/gl/preamble.fs.glsl");
GSK_GL_COMPILER_FRAGMENT,
"/org/gtk/libgsk/gl/preamble.fs.glsl");
/* Setup attributes that are provided via VBO */
gsk_gl_compiler_bind_attribute (compiler, "aPosition", 0);
@ -365,10 +347,28 @@ gsk_gl_driver_load_programs (GskGLDriver *self,
/* Use XMacros to register all of our programs and their uniforms */
#define GSK_GL_NO_UNIFORMS
#define GSK_GL_SHADER_RESOURCE(name) \
g_bytes_ref(g_resources_lookup_data("/org/gtk/libgsk/gl/" name, 0, NULL))
#define GSK_GL_SHADER_STRING(str) \
g_bytes_new_static(str, strlen(str))
#define GSK_GL_SHADER_SINGLE(bytes) \
G_STMT_START { \
GBytes *b = bytes; \
gsk_gl_compiler_set_source (compiler, GSK_GL_COMPILER_ALL, b); \
g_bytes_unref (b); \
} G_STMT_END;
#define GSK_GL_SHADER_JOINED(kind, ...) \
G_STMT_START { \
GBytes *bytes = join_sources(__VA_ARGS__); \
gsk_gl_compiler_set_source (compiler, GSK_GL_COMPILER_##kind, bytes); \
g_bytes_unref (bytes); \
} G_STMT_END;
#define GSK_GL_ADD_UNIFORM(pos, KEY, name) \
gsk_gl_program_add_uniform (program, #name, UNIFORM_##KEY);
#define GSK_GL_DEFINE_PROGRAM(name, resource, uniforms) \
gsk_gl_compiler_set_source_from_resource (compiler, GSK_GL_COMPILER_ALL, resource); \
#define GSK_GL_DEFINE_PROGRAM(name, sources, uniforms) \
gsk_gl_compiler_set_source (compiler, GSK_GL_COMPILER_VERTEX, NULL); \
gsk_gl_compiler_set_source (compiler, GSK_GL_COMPILER_FRAGMENT, NULL); \
sources \
GSK_GL_COMPILE_PROGRAM(name ## _no_clip, uniforms, "#define NO_CLIP 1\n"); \
GSK_GL_COMPILE_PROGRAM(name ## _rect_clip, uniforms, "#define RECT_CLIP 1\n"); \
GSK_GL_COMPILE_PROGRAM(name, uniforms, "");
@ -401,6 +401,11 @@ gsk_gl_driver_load_programs (GskGLDriver *self,
#undef GSK_GL_DEFINE_PROGRAM_CLIP
#undef GSK_GL_DEFINE_PROGRAM
#undef GSK_GL_ADD_UNIFORM
#undef GSK_GL_SHADER_SINGLE
#undef GSK_GL_SHADER_JOINED
#undef GSK_GL_SHADER_RESOURCE
#undef GSK_GL_SHADER_STRING
#undef GSK_GL_NO_UNIFORMS
ret = TRUE;
@ -456,9 +461,9 @@ gsk_gl_driver_new (GskGLCommandQueue *command_queue,
return NULL;
}
self->glyphs = gsk_gl_glyph_library_new (self);
self->icons = gsk_gl_icon_library_new (self);
self->shadows = gsk_gl_shadow_library_new (self);
self->glyphs_library = gsk_gl_glyph_library_new (self);
self->icons_library = gsk_gl_icon_library_new (self);
self->shadows_library = gsk_gl_shadow_library_new (self);
gdk_profiler_end_mark (before, "create GskGLDriver", NULL);
@ -518,37 +523,6 @@ failure:
return g_steal_pointer (&driver);
}
static GPtrArray *
gsk_gl_driver_compact_atlases (GskGLDriver *self)
{
GPtrArray *removed = NULL;
g_assert (GSK_IS_GL_DRIVER (self));
for (guint i = self->atlases->len; i > 0; i--)
{
GskGLTextureAtlas *atlas = g_ptr_array_index (self->atlases, i - 1);
if (gsk_gl_texture_atlas_get_unused_ratio (atlas) > MAX_OLD_RATIO)
{
GSK_NOTE (GLYPH_CACHE,
g_message ("Dropping atlas %d (%g.2%% old)", i,
100.0 * gsk_gl_texture_atlas_get_unused_ratio (atlas)));
if (removed == NULL)
removed = g_ptr_array_new_with_free_func ((GDestroyNotify)gsk_gl_texture_atlas_free);
g_ptr_array_add (removed, g_ptr_array_steal_index (self->atlases, i - 1));
}
}
GSK_NOTE (GLYPH_CACHE, {
static guint timestamp;
if (timestamp++ % 60 == 0)
g_message ("%d atlases", self->atlases->len);
});
return removed;
}
/**
* gsk_gl_driver_begin_frame:
* @self: a `GskGLDriver`
@ -565,7 +539,6 @@ gsk_gl_driver_begin_frame (GskGLDriver *self,
GskGLCommandQueue *command_queue)
{
gint64 last_frame_id;
GPtrArray *removed;
g_return_if_fail (GSK_IS_GL_DRIVER (self));
g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (command_queue));
@ -580,19 +553,14 @@ gsk_gl_driver_begin_frame (GskGLDriver *self,
gsk_gl_command_queue_begin_frame (self->command_queue);
/* Compact atlases with too many freed pixels */
removed = gsk_gl_driver_compact_atlases (self);
/* Mark unused pixel regions of the atlases */
gsk_gl_texture_library_begin_frame (GSK_GL_TEXTURE_LIBRARY (self->icons),
self->current_frame_id,
removed);
gsk_gl_texture_library_begin_frame (GSK_GL_TEXTURE_LIBRARY (self->glyphs),
self->current_frame_id,
removed);
gsk_gl_texture_library_begin_frame (GSK_GL_TEXTURE_LIBRARY (self->icons_library),
self->current_frame_id);
gsk_gl_texture_library_begin_frame (GSK_GL_TEXTURE_LIBRARY (self->glyphs_library),
self->current_frame_id);
/* Cleanup old shadows */
gsk_gl_shadow_library_begin_frame (self->shadows);
gsk_gl_shadow_library_begin_frame (self->shadows_library);
/* Remove all textures that are from a previous frame or are no
* longer used by linked GdkTexture. We do this at the beginning
@ -600,9 +568,6 @@ gsk_gl_driver_begin_frame (GskGLDriver *self,
* we block on any resources while delivering our frames.
*/
gsk_gl_driver_collect_unused_textures (self, last_frame_id - 1);
/* Now free atlas textures */
g_clear_pointer (&removed, g_ptr_array_unref);
}
/**
@ -1182,14 +1147,23 @@ void
gsk_gl_driver_save_atlases_to_png (GskGLDriver *self,
const char *directory)
{
GPtrArray *atlases;
g_return_if_fail (GSK_IS_GL_DRIVER (self));
if (directory == NULL)
directory = ".";
for (guint i = 0; i < self->atlases->len; i++)
#define copy_atlases(dst, library) \
g_ptr_array_extend(dst, GSK_GL_TEXTURE_LIBRARY(library)->atlases, NULL, NULL)
atlases = g_ptr_array_new ();
copy_atlases (atlases, self->glyphs_library);
copy_atlases (atlases, self->icons_library);
#undef copy_atlases
for (guint i = 0; i < atlases->len; i++)
{
GskGLTextureAtlas *atlas = g_ptr_array_index (self->atlases, i);
GskGLTextureAtlas *atlas = g_ptr_array_index (atlases, i);
char *filename = g_strdup_printf ("%s%sframe-%d-atlas-%d.png",
directory,
G_DIR_SEPARATOR_S,
@ -1198,6 +1172,8 @@ gsk_gl_driver_save_atlases_to_png (GskGLDriver *self,
write_atlas_to_png (self, atlas, filename);
g_free (filename);
}
g_ptr_array_unref (atlases);
}
#endif

View File

@ -100,17 +100,15 @@ struct _GskGLDriver
GskGLCommandQueue *shared_command_queue;
GskGLCommandQueue *command_queue;
GskGLGlyphLibrary *glyphs;
GskGLIconLibrary *icons;
GskGLShadowLibrary *shadows;
GskGLGlyphLibrary *glyphs_library;
GskGLIconLibrary *icons_library;
GskGLShadowLibrary *shadows_library;
GArray *texture_pool;
GHashTable *textures;
GHashTable *key_to_texture_id;
GHashTable *texture_id_to_key;
GPtrArray *atlases;
GHashTable *shader_cache;
GArray *autorelease_framebuffers;
@ -184,7 +182,6 @@ void gsk_gl_driver_add_texture_slices (GskGLDriver *s
GskGLProgram * gsk_gl_driver_lookup_shader (GskGLDriver *self,
GskGLShader *shader,
GError **error);
GskGLTextureAtlas * gsk_gl_driver_create_atlas (GskGLDriver *self);
#ifdef G_ENABLE_DEBUG
void gsk_gl_driver_save_atlases_to_png (GskGLDriver *self,

View File

@ -84,15 +84,64 @@ gsk_gl_glyph_value_free (gpointer data)
}
static void
gsk_gl_glyph_library_begin_frame (GskGLTextureLibrary *library,
gint64 frame_id,
GPtrArray *removed_atlases)
gsk_gl_glyph_library_clear_cache (GskGLTextureLibrary *library)
{
GskGLGlyphLibrary *self = (GskGLGlyphLibrary *)library;
g_assert (GSK_IS_GL_GLYPH_LIBRARY (self));
memset (self->front, 0, sizeof self->front);
}
static void
gsk_gl_glyph_library_init_atlas (GskGLTextureLibrary *self,
GskGLTextureAtlas *atlas)
{
gboolean packed G_GNUC_UNUSED;
int x, y;
guint gl_format;
guint gl_type;
guint8 pixel_data[4 * 3 * 3];
g_assert (GSK_IS_GL_GLYPH_LIBRARY (self));
g_assert (atlas != NULL);
/* Insert a single pixel at 0,0 for use in coloring */
gdk_gl_context_push_debug_group_printf (gdk_gl_context_get_current (),
"Initializing Atlas");
packed = gsk_gl_texture_library_allocate (self, atlas, 3, 3, &x, &y);
g_assert (packed);
g_assert (x == 0 && y == 0);
memset (pixel_data, 255, sizeof pixel_data);
if (gdk_gl_context_get_use_es (gdk_gl_context_get_current ()))
{
gl_format = GL_RGBA;
gl_type = GL_UNSIGNED_BYTE;
}
else
{
gl_format = GL_BGRA;
gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
}
glBindTexture (GL_TEXTURE_2D, atlas->texture_id);
glTexSubImage2D (GL_TEXTURE_2D, 0,
0, 0,
3, 3,
gl_format, gl_type,
pixel_data);
gdk_gl_context_pop_debug_group (gdk_gl_context_get_current ());
self->driver->command_queue->n_uploads++;
}
static void
gsk_gl_glyph_library_finalize (GObject *object)
{
@ -111,7 +160,8 @@ gsk_gl_glyph_library_class_init (GskGLGlyphLibraryClass *klass)
object_class->finalize = gsk_gl_glyph_library_finalize;
library_class->begin_frame = gsk_gl_glyph_library_begin_frame;
library_class->clear_cache = gsk_gl_glyph_library_clear_cache;
library_class->init_atlas = gsk_gl_glyph_library_init_atlas;
}
static void

View File

@ -1,71 +1,71 @@
GSK_GL_DEFINE_PROGRAM (blend,
"/org/gtk/libgsk/gl/blend.glsl",
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("blend.glsl")),
GSK_GL_ADD_UNIFORM (1, BLEND_SOURCE2, u_source2)
GSK_GL_ADD_UNIFORM (2, BLEND_MODE, u_mode))
GSK_GL_DEFINE_PROGRAM (blit,
"/org/gtk/libgsk/gl/blit.glsl",
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("blit.glsl")),
GSK_GL_NO_UNIFORMS)
GSK_GL_DEFINE_PROGRAM (blur,
"/org/gtk/libgsk/gl/blur.glsl",
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("blur.glsl")),
GSK_GL_ADD_UNIFORM (1, BLUR_RADIUS, u_blur_radius)
GSK_GL_ADD_UNIFORM (2, BLUR_SIZE, u_blur_size)
GSK_GL_ADD_UNIFORM (3, BLUR_DIR, u_blur_dir))
GSK_GL_DEFINE_PROGRAM (border,
"/org/gtk/libgsk/gl/border.glsl",
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("border.glsl")),
GSK_GL_ADD_UNIFORM (1, BORDER_WIDTHS, u_widths)
GSK_GL_ADD_UNIFORM (2, BORDER_OUTLINE_RECT, u_outline_rect))
GSK_GL_DEFINE_PROGRAM (color,
"/org/gtk/libgsk/gl/color.glsl",
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("color.glsl")),
GSK_GL_NO_UNIFORMS)
GSK_GL_DEFINE_PROGRAM (coloring,
"/org/gtk/libgsk/gl/coloring.glsl",
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("coloring.glsl")),
GSK_GL_NO_UNIFORMS)
GSK_GL_DEFINE_PROGRAM (color_matrix,
"/org/gtk/libgsk/gl/color_matrix.glsl",
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("color_matrix.glsl")),
GSK_GL_ADD_UNIFORM (1, COLOR_MATRIX_COLOR_MATRIX, u_color_matrix)
GSK_GL_ADD_UNIFORM (2, COLOR_MATRIX_COLOR_OFFSET, u_color_offset))
GSK_GL_DEFINE_PROGRAM (conic_gradient,
"/org/gtk/libgsk/gl/conic_gradient.glsl",
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("conic_gradient.glsl")),
GSK_GL_ADD_UNIFORM (1, CONIC_GRADIENT_COLOR_STOPS, u_color_stops)
GSK_GL_ADD_UNIFORM (2, CONIC_GRADIENT_NUM_COLOR_STOPS, u_num_color_stops)
GSK_GL_ADD_UNIFORM (3, CONIC_GRADIENT_GEOMETRY, u_geometry))
GSK_GL_DEFINE_PROGRAM (cross_fade,
"/org/gtk/libgsk/gl/cross_fade.glsl",
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("cross_fade.glsl")),
GSK_GL_ADD_UNIFORM (1, CROSS_FADE_PROGRESS, u_progress)
GSK_GL_ADD_UNIFORM (2, CROSS_FADE_SOURCE2, u_source2))
GSK_GL_DEFINE_PROGRAM (filled_border,
"/org/gtk/libgsk/gl/filled_border.glsl",
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("filled_border.glsl")),
GSK_GL_ADD_UNIFORM (1, FILLED_BORDER_WIDTHS, u_widths)
GSK_GL_ADD_UNIFORM (2, FILLED_BORDER_OUTLINE_RECT, u_outline_rect))
GSK_GL_DEFINE_PROGRAM (inset_shadow,
"/org/gtk/libgsk/gl/inset_shadow.glsl",
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("inset_shadow.glsl")),
GSK_GL_ADD_UNIFORM (1, INSET_SHADOW_SPREAD, u_spread)
GSK_GL_ADD_UNIFORM (2, INSET_SHADOW_OFFSET, u_offset)
GSK_GL_ADD_UNIFORM (3, INSET_SHADOW_OUTLINE_RECT, u_outline_rect))
GSK_GL_DEFINE_PROGRAM (linear_gradient,
"/org/gtk/libgsk/gl/linear_gradient.glsl",
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("linear_gradient.glsl")),
GSK_GL_ADD_UNIFORM (1, LINEAR_GRADIENT_COLOR_STOPS, u_color_stops)
GSK_GL_ADD_UNIFORM (2, LINEAR_GRADIENT_NUM_COLOR_STOPS, u_num_color_stops)
GSK_GL_ADD_UNIFORM (3, LINEAR_GRADIENT_POINTS, u_points)
GSK_GL_ADD_UNIFORM (4, LINEAR_GRADIENT_REPEAT, u_repeat))
GSK_GL_DEFINE_PROGRAM (outset_shadow,
"/org/gtk/libgsk/gl/outset_shadow.glsl",
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("outset_shadow.glsl")),
GSK_GL_ADD_UNIFORM (1, OUTSET_SHADOW_OUTLINE_RECT, u_outline_rect))
GSK_GL_DEFINE_PROGRAM (radial_gradient,
"/org/gtk/libgsk/gl/radial_gradient.glsl",
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("radial_gradient.glsl")),
GSK_GL_ADD_UNIFORM (1, RADIAL_GRADIENT_COLOR_STOPS, u_color_stops)
GSK_GL_ADD_UNIFORM (2, RADIAL_GRADIENT_NUM_COLOR_STOPS, u_num_color_stops)
GSK_GL_ADD_UNIFORM (3, RADIAL_GRADIENT_REPEAT, u_repeat)
@ -73,12 +73,12 @@ GSK_GL_DEFINE_PROGRAM (radial_gradient,
GSK_GL_ADD_UNIFORM (5, RADIAL_GRADIENT_GEOMETRY, u_geometry))
GSK_GL_DEFINE_PROGRAM (repeat,
"/org/gtk/libgsk/gl/repeat.glsl",
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("repeat.glsl")),
GSK_GL_ADD_UNIFORM (1, REPEAT_CHILD_BOUNDS, u_child_bounds)
GSK_GL_ADD_UNIFORM (2, REPEAT_TEXTURE_RECT, u_texture_rect))
GSK_GL_DEFINE_PROGRAM (unblurred_outset_shadow,
"/org/gtk/libgsk/gl/unblurred_outset_shadow.glsl",
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("unblurred_outset_shadow.glsl")),
GSK_GL_ADD_UNIFORM (1, UNBLURRED_OUTSET_SHADOW_SPREAD, u_spread)
GSK_GL_ADD_UNIFORM (2, UNBLURRED_OUTSET_SHADOW_OFFSET, u_offset)
GSK_GL_ADD_UNIFORM (3, UNBLURRED_OUTSET_SHADOW_OUTLINE_RECT, u_outline_rect))

View File

@ -2512,7 +2512,9 @@ gsk_gl_render_job_visit_blurred_outset_shadow_node (GskGLRenderJob *job,
scaled_outline.corner[i].height *= scale_y;
}
cached_tid = gsk_gl_shadow_library_lookup (job->driver->shadows, &scaled_outline, blur_radius);
cached_tid = gsk_gl_shadow_library_lookup (job->driver->shadows_library,
&scaled_outline,
blur_radius);
if (cached_tid == 0)
{
@ -2574,7 +2576,7 @@ gsk_gl_render_job_visit_blurred_outset_shadow_node (GskGLRenderJob *job,
blur_radius * scale_x,
blur_radius * scale_y);
gsk_gl_shadow_library_insert (job->driver->shadows,
gsk_gl_shadow_library_insert (job->driver->shadows_library,
&scaled_outline,
blur_radius,
blurred_texture_id);
@ -2996,7 +2998,7 @@ gsk_gl_render_job_visit_text_node (GskGLRenderJob *job,
guint num_glyphs = gsk_text_node_get_num_glyphs (node);
float x = offset->x + job->offset_x;
float y = offset->y + job->offset_y;
GskGLGlyphLibrary *library = job->driver->glyphs;
GskGLGlyphLibrary *library = job->driver->glyphs_library;
GskGLCommandBatch *batch;
int x_position = 0;
GskGLGlyphKey lookup;
@ -3499,14 +3501,14 @@ gsk_gl_render_job_upload_texture (GskGLRenderJob *job,
GdkTexture *texture,
GskGLRenderOffscreen *offscreen)
{
if (gsk_gl_texture_library_can_cache ((GskGLTextureLibrary *)job->driver->icons,
if (gsk_gl_texture_library_can_cache ((GskGLTextureLibrary *)job->driver->icons_library,
texture->width,
texture->height) &&
!GDK_IS_GL_TEXTURE (texture))
{
const GskGLIconData *icon_data;
gsk_gl_icon_library_lookup_or_add (job->driver->icons, texture, &icon_data);
gsk_gl_icon_library_lookup_or_add (job->driver->icons_library, texture, &icon_data);
offscreen->texture_id = GSK_GL_TEXTURE_ATLAS_ENTRY_TEXTURE (icon_data);
memcpy (&offscreen->area, &icon_data->entry.area, sizeof offscreen->area);
}

View File

@ -29,9 +29,9 @@
struct _GskGLShadowLibrary
{
GObject parent_instance;
GObject parent_instance;
GskGLDriver *driver;
GArray *shadows;
GArray *shadows;
};
typedef struct _Shadow

View File

@ -27,7 +27,10 @@
#include "gskgldriverprivate.h"
#include "gskgltexturelibraryprivate.h"
#define MAX_FRAME_AGE 60
#define DEFAULT_MAX_FRAME_AGE 60
#define DEFAULT_ATLAS_WIDTH 512
#define DEFAULT_ATLAS_HEIGHT 512
#define MAX_OLD_RATIO 0.5
G_DEFINE_ABSTRACT_TYPE (GskGLTextureLibrary, gsk_gl_texture_library, G_TYPE_OBJECT)
@ -39,6 +42,143 @@ enum {
static GParamSpec *properties [N_PROPS];
static void
gsk_gl_texture_atlas_free (GskGLTextureAtlas *atlas)
{
if (atlas->texture_id != 0)
{
glDeleteTextures (1, &atlas->texture_id);
atlas->texture_id = 0;
}
g_clear_pointer (&atlas->nodes, g_free);
g_slice_free (GskGLTextureAtlas, atlas);
}
static gboolean
gsk_gl_texture_library_real_compact (GskGLTextureLibrary *self,
gint64 frame_id)
{
GPtrArray *removed = NULL;
gboolean ret = FALSE;
gboolean periodic_scan;
g_assert (GSK_IS_GL_TEXTURE_LIBRARY (self));
periodic_scan = (self->max_frame_age > 0 &&
(frame_id % self->max_frame_age) == 0);
for (guint i = self->atlases->len; i > 0; i--)
{
GskGLTextureAtlas *atlas = g_ptr_array_index (self->atlases, i - 1);
if (gsk_gl_texture_atlas_get_unused_ratio (atlas) > MAX_OLD_RATIO)
{
GSK_NOTE (GLYPH_CACHE,
g_message ("Dropping atlas %d (%g.2%% old)", i,
100.0 * gsk_gl_texture_atlas_get_unused_ratio (atlas)));
if (removed == NULL)
removed = g_ptr_array_new_with_free_func ((GDestroyNotify)gsk_gl_texture_atlas_free);
g_ptr_array_add (removed, g_ptr_array_steal_index (self->atlases, i - 1));
}
}
if (periodic_scan || removed != NULL)
{
GskGLTextureAtlasEntry *entry;
GHashTableIter iter;
guint dropped = 0;
guint atlased = 0;
g_hash_table_iter_init (&iter, self->hash_table);
while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&entry))
{
if (entry->is_atlased)
{
if (removed && g_ptr_array_find (removed, entry->atlas, NULL))
{
g_hash_table_iter_remove (&iter);
dropped++;
}
else if (periodic_scan)
{
gsk_gl_texture_atlas_entry_mark_unused (entry);
entry->accessed = FALSE;
if (entry->is_atlased)
atlased++;
}
}
else if (!entry->accessed)
{
gsk_gl_driver_release_texture (self->driver, entry->texture);
g_hash_table_iter_remove (&iter);
dropped++;
}
}
GSK_NOTE (GLYPH_CACHE, g_message ("%s: Dropped %d individual items",
G_OBJECT_TYPE_NAME (self),
dropped);
g_message ("%s: %d items cached (%d atlased, %d individually)",
G_OBJECT_TYPE_NAME (self),
g_hash_table_size (self->hash_table),
atlased,
g_hash_table_size (self->hash_table) - atlased));
if (dropped > 0)
gsk_gl_texture_library_clear_cache (self);
ret = TRUE;
g_clear_pointer (&removed, g_ptr_array_unref);
}
GSK_NOTE (GLYPH_CACHE, {
static gint64 last_message;
gint64 now = g_get_monotonic_time ();
if (now - last_message > G_USEC_PER_SEC)
{
last_message = now;
g_message ("%s contains %d atlases",
G_OBJECT_TYPE_NAME (self),
self->atlases->len);
}
});
return ret;
}
static gboolean
gsk_gl_texture_library_real_allocate (GskGLTextureLibrary *self,
GskGLTextureAtlas *atlas,
int width,
int height,
int *out_x,
int *out_y)
{
stbrp_rect rect;
g_assert (GSK_IS_GL_TEXTURE_LIBRARY (self));
g_assert (atlas != NULL);
g_assert (width > 0);
g_assert (height > 0);
g_assert (out_x != NULL);
g_assert (out_y != NULL);
rect.w = width;
rect.h = height;
stbrp_pack_rects (&atlas->context, &rect, 1);
if (rect.was_packed)
{
*out_x = rect.x;
*out_y = rect.y;
}
return rect.was_packed;
}
static void
gsk_gl_texture_library_constructed (GObject *object)
{
@ -52,6 +192,7 @@ gsk_gl_texture_library_dispose (GObject *object)
{
GskGLTextureLibrary *self = (GskGLTextureLibrary *)object;
g_clear_pointer (&self->atlases, g_ptr_array_unref);
g_clear_object (&self->driver);
G_OBJECT_CLASS (gsk_gl_texture_library_parent_class)->dispose (object);
@ -105,6 +246,9 @@ gsk_gl_texture_library_class_init (GskGLTextureLibraryClass *klass)
object_class->get_property = gsk_gl_texture_library_get_property;
object_class->set_property = gsk_gl_texture_library_set_property;
klass->compact = gsk_gl_texture_library_real_compact;
klass->allocate = gsk_gl_texture_library_real_allocate;
properties [PROP_DRIVER] =
g_param_spec_object ("driver",
"Driver",
@ -118,6 +262,10 @@ gsk_gl_texture_library_class_init (GskGLTextureLibraryClass *klass)
static void
gsk_gl_texture_library_init (GskGLTextureLibrary *self)
{
self->max_frame_age = DEFAULT_MAX_FRAME_AGE;
self->atlas_width = DEFAULT_ATLAS_WIDTH;
self->atlas_height = DEFAULT_ATLAS_HEIGHT;
self->atlases = g_ptr_array_new_with_free_func ((GDestroyNotify)gsk_gl_texture_atlas_free);
}
void
@ -136,78 +284,14 @@ gsk_gl_texture_library_set_funcs (GskGLTextureLibrary *self,
void
gsk_gl_texture_library_begin_frame (GskGLTextureLibrary *self,
gint64 frame_id,
GPtrArray *removed_atlases)
gint64 frame_id)
{
GHashTableIter iter;
g_return_if_fail (GSK_IS_GL_TEXTURE_LIBRARY (self));
gsk_gl_texture_library_compact (self, frame_id);
if (GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->begin_frame)
GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->begin_frame (self, frame_id, removed_atlases);
if (removed_atlases != NULL)
{
GskGLTextureAtlasEntry *entry;
guint dropped = 0;
g_hash_table_iter_init (&iter, self->hash_table);
while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&entry))
{
if (entry->is_atlased)
{
for (guint i = 0; i < removed_atlases->len; i++)
{
GskGLTextureAtlas *atlas = g_ptr_array_index (removed_atlases, i);
if (atlas == entry->atlas)
{
g_hash_table_iter_remove (&iter);
dropped++;
break;
}
}
}
}
GSK_NOTE (GLYPH_CACHE,
if (dropped > 0)
g_message ("%s: Dropped %d items",
G_OBJECT_TYPE_NAME (self), dropped));
}
if (frame_id % MAX_FRAME_AGE == 0)
{
GskGLTextureAtlasEntry *entry;
int atlased = 0;
int dropped = 0;
g_hash_table_iter_init (&iter, self->hash_table);
while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&entry))
{
if (!entry->is_atlased && !entry->accessed)
{
gsk_gl_driver_release_texture (self->driver, entry->texture);
g_hash_table_iter_remove (&iter);
dropped++;
continue;
}
gsk_gl_texture_atlas_entry_mark_unused (entry);
entry->accessed = FALSE;
if (entry->is_atlased)
atlased++;
}
GSK_NOTE (GLYPH_CACHE, g_message ("%s: Dropped %d individual items",
G_OBJECT_TYPE_NAME (self),
dropped);
g_message ("%s: %d items cached (%d atlased, %d individually)",
G_OBJECT_TYPE_NAME (self),
g_hash_table_size (self->hash_table),
atlased,
g_hash_table_size (self->hash_table) - atlased));
}
GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->begin_frame (self, frame_id);
}
static GskGLTexture *
@ -234,90 +318,29 @@ gsk_gl_texture_library_pack_one (GskGLTextureLibrary *self,
return texture;
}
static inline gboolean
gsk_gl_texture_atlas_pack (GskGLTextureAtlas *self,
int width,
int height,
int *out_x,
int *out_y)
{
stbrp_rect rect;
rect.w = width;
rect.h = height;
stbrp_pack_rects (&self->context, &rect, 1);
if (rect.was_packed)
{
*out_x = rect.x;
*out_y = rect.y;
}
return rect.was_packed;
}
static void
gsk_gl_texture_atlas_initialize (GskGLDriver *driver,
GskGLTextureAtlas *atlas)
{
/* Insert a single pixel at 0,0 for use in coloring */
gboolean packed G_GNUC_UNUSED;
int x, y;
guint gl_format;
guint gl_type;
guint8 pixel_data[4 * 3 * 3];
gdk_gl_context_push_debug_group_printf (gdk_gl_context_get_current (),
"Initializing Atlas");
packed = gsk_gl_texture_atlas_pack (atlas, 3, 3, &x, &y);
g_assert (packed);
g_assert (x == 0 && y == 0);
memset (pixel_data, 255, sizeof pixel_data);
if (gdk_gl_context_get_use_es (gdk_gl_context_get_current ()))
{
gl_format = GL_RGBA;
gl_type = GL_UNSIGNED_BYTE;
}
else
{
gl_format = GL_BGRA;
gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
}
glBindTexture (GL_TEXTURE_2D, atlas->texture_id);
glTexSubImage2D (GL_TEXTURE_2D, 0,
0, 0,
3, 3,
gl_format, gl_type,
pixel_data);
gdk_gl_context_pop_debug_group (gdk_gl_context_get_current ());
driver->command_queue->n_uploads++;
}
static void
gsk_gl_texture_atlases_pack (GskGLDriver *driver,
int width,
int height,
GskGLTextureAtlas **out_atlas,
int *out_x,
int *out_y)
gsk_gl_texture_library_pack_any_atlas (GskGLTextureLibrary *self,
int width,
int height,
GskGLTextureAtlas **out_atlas,
int *out_x,
int *out_y)
{
GskGLTextureAtlas *atlas = NULL;
int x, y;
for (guint i = 0; i < driver->atlases->len; i++)
{
atlas = g_ptr_array_index (driver->atlases, i);
g_assert (GSK_IS_GL_TEXTURE_LIBRARY (self));
g_assert (width > 0);
g_assert (height > 0);
g_assert (out_atlas != NULL);
g_assert (out_x != NULL);
g_assert (out_y != NULL);
if (gsk_gl_texture_atlas_pack (atlas, width, height, &x, &y))
for (guint i = 0; i < self->atlases->len; i++)
{
atlas = g_ptr_array_index (self->atlases, i);
if (gsk_gl_texture_library_allocate (self, atlas, width, height, &x, &y))
break;
atlas = NULL;
@ -326,12 +349,10 @@ gsk_gl_texture_atlases_pack (GskGLDriver *driver,
if (atlas == NULL)
{
/* No atlas has enough space, so create a new one... */
atlas = gsk_gl_driver_create_atlas (driver);
gsk_gl_texture_atlas_initialize (driver, atlas);
atlas = gsk_gl_texture_library_acquire_atlas (self);
/* Pack it onto that one, which surely has enough space... */
if (!gsk_gl_texture_atlas_pack (atlas, width, height, &x, &y))
if (!gsk_gl_texture_library_allocate (self, atlas, width, height, &x, &y))
g_assert_not_reached ();
}
@ -380,17 +401,19 @@ gsk_gl_texture_library_pack (GskGLTextureLibrary *self,
*out_packed_x = 0;
*out_packed_y = 0;
}
else if (width <= self->max_entry_size && height <= self->max_entry_size)
else if (self->max_entry_size == 0 ||
(width <= self->max_entry_size &&
height <= self->max_entry_size))
{
int packed_x;
int packed_y;
gsk_gl_texture_atlases_pack (self->driver,
padding + width + padding,
padding + height + padding,
&atlas,
&packed_x,
&packed_y);
gsk_gl_texture_library_pack_any_atlas (self,
padding + width + padding,
padding + height + padding,
&atlas,
&packed_x,
&packed_y);
entry->atlas = atlas;
entry->is_atlased = TRUE;
@ -424,3 +447,134 @@ gsk_gl_texture_library_pack (GskGLTextureLibrary *self,
return entry;
}
/*
* gsk_gl_texture_library_clear_cache:
*
* Clear the front cache if the texture library is using one. For
* example the glyph cache would drop it's front cache to force
* next lookups to fall through to the GHashTable key lookup.
*/
void
gsk_gl_texture_library_clear_cache (GskGLTextureLibrary *self)
{
g_return_if_fail (GSK_IS_GL_TEXTURE_LIBRARY (self));
if (GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->clear_cache)
GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->clear_cache (self);
}
/*
* gsk_gl_texture_library_compact:
*
* Requests that the texture library compact it's altases. That
* generally means to traverse them to look for unused pixels over
* a certain threshold and release them if necessary.
*
* Returns: %TRUE if any compaction occurred.
*/
gboolean
gsk_gl_texture_library_compact (GskGLTextureLibrary *self,
gint64 frame_id)
{
g_return_val_if_fail (GSK_IS_GL_TEXTURE_LIBRARY (self), FALSE);
if (GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->compact)
return GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->compact (self, frame_id);
return FALSE;
}
void
gsk_gl_texture_library_reset (GskGLTextureLibrary *self)
{
g_return_if_fail (GSK_IS_GL_TEXTURE_LIBRARY (self));
gsk_gl_texture_library_clear_cache (self);
g_hash_table_remove_all (self->hash_table);
if (self->atlases->len)
g_ptr_array_remove_range (self->atlases, 0, self->atlases->len);
}
void
gsk_gl_texture_library_set_atlas_size (GskGLTextureLibrary *self,
int width,
int height)
{
g_return_if_fail (GSK_IS_GL_TEXTURE_LIBRARY (self));
if (width <= 0)
width = DEFAULT_ATLAS_WIDTH;
if (height <= 0)
height = DEFAULT_ATLAS_HEIGHT;
self->atlas_height = height;
self->atlas_width = width;
gsk_gl_texture_library_reset (self);
}
/*
* gsk_gl_texture_library_acquire_atlas:
*
* Allocates a new texture atlas based on the current size
* and format requirements.
*/
GskGLTextureAtlas *
gsk_gl_texture_library_acquire_atlas (GskGLTextureLibrary *self)
{
GskGLTextureAtlas *atlas;
g_return_val_if_fail (GSK_IS_GL_TEXTURE_LIBRARY (self), NULL);
g_return_val_if_fail (GSK_IS_GL_DRIVER (self->driver), NULL);
g_return_val_if_fail (GSK_IS_GL_COMMAND_QUEUE (self->driver->command_queue), NULL);
g_return_val_if_fail (self->atlas_width > 0, NULL);
g_return_val_if_fail (self->atlas_height > 0, NULL);
atlas = g_slice_new0 (GskGLTextureAtlas);
atlas->width = self->atlas_width;
atlas->height = self->atlas_height;
/* TODO: We might want to change the strategy about the amount of
* nodes here? stb_rect_pack.h says width is optimal. */
atlas->nodes = g_malloc0_n (atlas->width, sizeof (struct stbrp_node));
stbrp_init_target (&atlas->context, atlas->width, atlas->height, atlas->nodes, atlas->width);
atlas->texture_id = gsk_gl_command_queue_create_texture (self->driver->command_queue,
atlas->width,
atlas->height,
GL_RGBA8,
GL_LINEAR,
GL_LINEAR);
gdk_gl_context_label_object_printf (gdk_gl_context_get_current (),
GL_TEXTURE, atlas->texture_id,
"Texture atlas %d",
atlas->texture_id);
g_ptr_array_add (self->atlases, atlas);
if (GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->init_atlas)
GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->init_atlas (self, atlas);
return atlas;
}
gboolean
gsk_gl_texture_library_allocate (GskGLTextureLibrary *self,
GskGLTextureAtlas *atlas,
int width,
int height,
int *out_x,
int *out_y)
{
g_assert (GSK_IS_GL_TEXTURE_LIBRARY (self));
g_assert (atlas != NULL);
g_assert (width > 0);
g_assert (height > 0);
g_assert (out_x != NULL);
g_assert (out_y != NULL);
return GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->allocate (self, atlas, width, height, out_x, out_y);
}

View File

@ -50,7 +50,6 @@ typedef struct _GskGLTextureAtlas
*/
int unused_pixels;
void *user_data;
} GskGLTextureAtlas;
typedef struct _GskGLTextureAtlasEntry
@ -89,40 +88,67 @@ typedef struct _GskGLTextureAtlasEntry
typedef struct _GskGLTextureLibrary
{
GObject parent_instance;
GObject parent_instance;
GskGLDriver *driver;
GHashTable *hash_table;
guint max_entry_size;
GPtrArray *atlases;
GHashTable *hash_table;
guint max_entry_size;
guint max_frame_age;
guint atlas_width;
guint atlas_height;
} GskGLTextureLibrary;
typedef struct _GskGLTextureLibraryClass
{
GObjectClass parent_class;
void (*begin_frame) (GskGLTextureLibrary *library,
gint64 frame_id,
GPtrArray *removed_atlases);
void (*begin_frame) (GskGLTextureLibrary *library,
gint64 frame_id);
gboolean (*compact) (GskGLTextureLibrary *library,
gint64 frame_id);
void (*clear_cache) (GskGLTextureLibrary *library);
void (*init_atlas) (GskGLTextureLibrary *library,
GskGLTextureAtlas *atlas);
gboolean (*allocate) (GskGLTextureLibrary *library,
GskGLTextureAtlas *atlas,
int width,
int height,
int *out_x,
int *out_y);
} GskGLTextureLibraryClass;
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GskGLTextureLibrary, g_object_unref)
GType gsk_gl_texture_library_get_type (void) G_GNUC_CONST;
void gsk_gl_texture_library_set_funcs (GskGLTextureLibrary *self,
GHashFunc hash_func,
GEqualFunc equal_func,
GDestroyNotify key_destroy,
GDestroyNotify value_destroy);
void gsk_gl_texture_library_begin_frame (GskGLTextureLibrary *self,
gint64 frame_id,
GPtrArray *removed_atlases);
gpointer gsk_gl_texture_library_pack (GskGLTextureLibrary *self,
gpointer key,
gsize valuelen,
guint width,
guint height,
int padding,
guint *out_packed_x,
guint *out_packed_y);
GType gsk_gl_texture_library_get_type (void) G_GNUC_CONST;
gboolean gsk_gl_texture_library_compact (GskGLTextureLibrary *self,
gint64 frame_id);
void gsk_gl_texture_library_clear_cache (GskGLTextureLibrary *self);
void gsk_gl_texture_library_reset (GskGLTextureLibrary *self);
void gsk_gl_texture_library_set_atlas_size (GskGLTextureLibrary *self,
int width,
int height);
GskGLTextureAtlas *gsk_gl_texture_library_acquire_atlas (GskGLTextureLibrary *self);
void gsk_gl_texture_library_set_funcs (GskGLTextureLibrary *self,
GHashFunc hash_func,
GEqualFunc equal_func,
GDestroyNotify key_destroy,
GDestroyNotify value_destroy);
void gsk_gl_texture_library_begin_frame (GskGLTextureLibrary *self,
gint64 frame_id);
gboolean gsk_gl_texture_library_allocate (GskGLTextureLibrary *self,
GskGLTextureAtlas *atlas,
int width,
int height,
int *out_x,
int *out_y);
gpointer gsk_gl_texture_library_pack (GskGLTextureLibrary *self,
gpointer key,
gsize valuelen,
guint width,
guint height,
int padding,
guint *out_packed_x,
guint *out_packed_y);
static inline void
gsk_gl_texture_atlas_mark_unused (GskGLTextureAtlas *self,