[gtk/wip/chergert/glproto: 715/920] start on texture atlasing plumbing from driver to libraries
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/wip/chergert/glproto: 715/920] start on texture atlasing plumbing from driver to libraries
- Date: Mon, 8 Feb 2021 19:15:38 +0000 (UTC)
commit fdcc39c3a9b8406c5204cb53df2f83a3736e83a8
Author: Christian Hergert <chergert redhat com>
Date: Mon Jan 18 21:42:42 2021 -0800
start on texture atlasing plumbing from driver to libraries
gsk/meson.build | 1 -
gsk/next/gskgldriver.c | 56 +++++++-
gsk/next/gskgldriverprivate.h | 81 +++++------
gsk/next/gskglglyphlibrary.c | 48 +++++--
gsk/next/gskglglyphlibraryprivate.h | 59 ++------
gsk/next/gskgliconlibrary.c | 7 +-
gsk/next/gskgliconlibraryprivate.h | 2 +-
gsk/next/gskglrenderjob.c | 25 ++--
gsk/next/gskglshadowlibrary.c | 7 +-
gsk/next/gskglshadowlibraryprivate.h | 2 +-
gsk/next/gskgltextureatlas.c | 252 ++++++++++++++++++++++++++++++----
gsk/next/gskgltextureatlasprivate.h | 56 ++++----
gsk/next/gskgltexturelibrary.c | 224 ++++++++++++++++++++++--------
gsk/next/gskgltexturelibraryprivate.h | 176 ++++++++++++++++++++----
gsk/next/gskgltypesprivate.h | 1 +
15 files changed, 738 insertions(+), 259 deletions(-)
---
diff --git a/gsk/meson.build b/gsk/meson.build
index 6c1f4c7566..62193a79da 100644
--- a/gsk/meson.build
+++ b/gsk/meson.build
@@ -59,7 +59,6 @@ gsk_private_sources = files([
'next/gskglprogram.c',
'next/gskglrenderjob.c',
'next/gskglshadowlibrary.c',
- 'next/gskgltextureatlas.c',
'next/gskgltexturelibrary.c',
'next/gskgluniformstate.c',
'next/gskgltexturepool.c',
diff --git a/gsk/next/gskgldriver.c b/gsk/next/gskgldriver.c
index 8ef7e28c5b..a0dfeba9eb 100644
--- a/gsk/next/gskgldriver.c
+++ b/gsk/next/gskgldriver.c
@@ -40,6 +40,7 @@
#include "gskgltexturepoolprivate.h"
#define TEXTURES_CACHED_FOR_N_FRAMES 1
+#define ATLAS_SIZE 512
G_DEFINE_TYPE (GskNextDriver, gsk_next_driver, G_TYPE_OBJECT)
@@ -132,6 +133,49 @@ gsk_next_driver_collect_unused_textures (GskNextDriver *self,
return old_size - g_hash_table_size (self->textures);
}
+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_next_driver_create_atlas (GskNextDriver *self)
+{
+ GskGLTextureAtlas *atlas;
+
+ g_return_val_if_fail (GSK_IS_NEXT_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_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
gsk_next_driver_shader_weak_cb (gpointer data,
GObject *where_object_was)
@@ -197,6 +241,11 @@ gsk_next_driver_dispose (GObject *object)
self->autorelease_framebuffers->len = 0;
}
+ g_clear_object (&self->glyphs);
+ g_clear_object (&self->icons);
+ g_clear_object (&self->shadows);
+
+ 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);
@@ -232,6 +281,7 @@ gsk_next_driver_init (GskNextDriver *self)
g_object_unref);
gsk_gl_texture_pool_init (&self->texture_pool);
self->render_targets = g_ptr_array_new ();
+ self->atlases = g_ptr_array_new_with_free_func ((GDestroyNotify)gsk_gl_texture_atlas_free);
}
static gboolean
@@ -342,9 +392,9 @@ gsk_next_driver_new (GskGLCommandQueue *command_queue,
return NULL;
}
- self->glyphs = gsk_gl_glyph_library_new (context);
- self->icons = gsk_gl_icon_library_new (context);
- self->shadows = gsk_gl_shadow_library_new (context);
+ self->glyphs = gsk_gl_glyph_library_new (self);
+ self->icons = gsk_gl_icon_library_new (self);
+ self->shadows = gsk_gl_shadow_library_new (self);
return g_steal_pointer (&self);
}
diff --git a/gsk/next/gskgldriverprivate.h b/gsk/next/gskgldriverprivate.h
index 6e843e0a93..120409d509 100644
--- a/gsk/next/gskgldriverprivate.h
+++ b/gsk/next/gskgldriverprivate.h
@@ -95,6 +95,8 @@ struct _GskNextDriver
GHashTable *key_to_texture_id;
GHashTable *texture_id_to_key;
+ GPtrArray *atlases;
+
GHashTable *shader_cache;
GArray *autorelease_framebuffers;
@@ -114,45 +116,46 @@ struct _GskNextDriver
guint in_frame : 1;
};
-GskNextDriver *gsk_next_driver_new (GskGLCommandQueue *command_queue,
- gboolean debug,
- GError **error);
-GdkGLContext *gsk_next_driver_get_context (GskNextDriver *self);
-gboolean gsk_next_driver_create_render_target (GskNextDriver *self,
- int width,
- int height,
- int min_filter,
- int mag_filter,
- GskGLRenderTarget **render_target);
-guint gsk_next_driver_release_render_target (GskNextDriver *self,
- GskGLRenderTarget *render_target,
- gboolean release_texture);
-void gsk_next_driver_begin_frame (GskNextDriver *self);
-void gsk_next_driver_end_frame (GskNextDriver *self);
-guint gsk_next_driver_lookup_texture (GskNextDriver *self,
- const GskTextureKey *key);
-void gsk_next_driver_cache_texture (GskNextDriver *self,
- const GskTextureKey *key,
- guint texture_id);
-guint gsk_next_driver_load_texture (GskNextDriver *self,
- GdkTexture *texture,
- int min_filter,
- int mag_filter);
-GskGLTexture *gsk_next_driver_create_texture (GskNextDriver *self,
- float width,
- float height,
- int min_filter,
- int mag_filter);
-GskGLTexture *gsk_next_driver_acquire_texture (GskNextDriver *self,
- float width,
- float height,
- int min_filter,
- int mag_filter);
-void gsk_next_driver_release_texture (GskNextDriver *self,
- GskGLTexture *texture);
-GskGLProgram *gsk_next_driver_lookup_shader (GskNextDriver *self,
- GskGLShader *shader,
- GError **error);
+GskNextDriver *gsk_next_driver_new (GskGLCommandQueue *command_queue,
+ gboolean debug,
+ GError **error);
+GdkGLContext *gsk_next_driver_get_context (GskNextDriver *self);
+gboolean gsk_next_driver_create_render_target (GskNextDriver *self,
+ int width,
+ int height,
+ int min_filter,
+ int mag_filter,
+ GskGLRenderTarget **render_target);
+guint gsk_next_driver_release_render_target (GskNextDriver *self,
+ GskGLRenderTarget *render_target,
+ gboolean release_texture);
+void gsk_next_driver_begin_frame (GskNextDriver *self);
+void gsk_next_driver_end_frame (GskNextDriver *self);
+guint gsk_next_driver_lookup_texture (GskNextDriver *self,
+ const GskTextureKey *key);
+void gsk_next_driver_cache_texture (GskNextDriver *self,
+ const GskTextureKey *key,
+ guint texture_id);
+guint gsk_next_driver_load_texture (GskNextDriver *self,
+ GdkTexture *texture,
+ int min_filter,
+ int mag_filter);
+GskGLTexture *gsk_next_driver_create_texture (GskNextDriver *self,
+ float width,
+ float height,
+ int min_filter,
+ int mag_filter);
+GskGLTexture *gsk_next_driver_acquire_texture (GskNextDriver *self,
+ float width,
+ float height,
+ int min_filter,
+ int mag_filter);
+void gsk_next_driver_release_texture (GskNextDriver *self,
+ GskGLTexture *texture);
+GskGLProgram *gsk_next_driver_lookup_shader (GskNextDriver *self,
+ GskGLShader *shader,
+ GError **error);
+GskGLTextureAtlas *gsk_next_driver_create_atlas (GskNextDriver *self);
G_END_DECLS
diff --git a/gsk/next/gskglglyphlibrary.c b/gsk/next/gskglglyphlibrary.c
index 858d72cfcb..3710697636 100644
--- a/gsk/next/gskglglyphlibrary.c
+++ b/gsk/next/gskglglyphlibrary.c
@@ -20,17 +20,20 @@
#include "config.h"
+#include "gskgldriverprivate.h"
#include "gskglglyphlibraryprivate.h"
+#define MAX_GLYPH_SIZE 128
+
G_DEFINE_TYPE (GskGLGlyphLibrary, gsk_gl_glyph_library, GSK_TYPE_GL_TEXTURE_LIBRARY)
GskGLGlyphLibrary *
-gsk_gl_glyph_library_new (GdkGLContext *context)
+gsk_gl_glyph_library_new (GskNextDriver *driver)
{
- g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
+ g_return_val_if_fail (GSK_IS_NEXT_DRIVER (driver), NULL);
return g_object_new (GSK_TYPE_GL_GLYPH_LIBRARY,
- "context", context,
+ "driver", driver,
NULL);
}
@@ -93,10 +96,12 @@ gsk_gl_glyph_library_class_init (GskGLGlyphLibraryClass *klass)
static void
gsk_gl_glyph_library_init (GskGLGlyphLibrary *self)
{
- self->hash_table = g_hash_table_new_full (gsk_gl_glyph_key_hash,
- gsk_gl_glyph_key_equal,
- gsk_gl_glyph_key_free,
- gsk_gl_glyph_value_free);
+ GSK_GL_TEXTURE_LIBRARY (self)->max_entry_size = MAX_GLYPH_SIZE;
+ gsk_gl_texture_library_set_funcs (GSK_GL_TEXTURE_LIBRARY (self),
+ gsk_gl_glyph_key_hash,
+ gsk_gl_glyph_key_equal,
+ gsk_gl_glyph_key_free,
+ gsk_gl_glyph_value_free);
}
gboolean
@@ -104,9 +109,36 @@ gsk_gl_glyph_library_add (GskGLGlyphLibrary *self,
const GskGLGlyphKey *key,
const GskGLGlyphValue **out_value)
{
+ PangoRectangle ink_rect;
+ GskGLGlyphValue *value;
+ int width;
+ int height;
+
g_assert (GSK_IS_GL_GLYPH_LIBRARY (self));
g_assert (key != NULL);
g_assert (out_value != NULL);
- return FALSE;
+ pango_font_get_glyph_extents (key->font, key->glyph, &ink_rect, NULL);
+ pango_extents_to_pixels (&ink_rect, NULL);
+
+ if (key->xshift != 0)
+ ink_rect.width++;
+ if (key->yshift != 0)
+ ink_rect.height++;
+
+ width = ink_rect.width * key->scale / 1024;
+ height = ink_rect.height * key->scale / 1024;
+
+ value = gsk_gl_texture_library_pack (GSK_GL_TEXTURE_LIBRARY (self),
+ key,
+ sizeof *key,
+ sizeof *value,
+ width,
+ height);
+
+ memcpy (&value->ink_rect, &ink_rect, sizeof ink_rect);
+
+
+
+ return TRUE;
}
diff --git a/gsk/next/gskglglyphlibraryprivate.h b/gsk/next/gskglglyphlibraryprivate.h
index dd17c516c3..b373339ce1 100644
--- a/gsk/next/gskglglyphlibraryprivate.h
+++ b/gsk/next/gskglglyphlibraryprivate.h
@@ -27,6 +27,8 @@
G_BEGIN_DECLS
+#define GSK_TYPE_GL_GLYPH_LIBRARY (gsk_gl_glyph_library_get_type())
+
typedef struct _GskGLGlyphKey
{
PangoFont *font;
@@ -36,33 +38,18 @@ typedef struct _GskGLGlyphKey
guint scale : 26; /* times 1024 */
} GskGLGlyphKey;
+typedef struct _GskGLGlyphValue
+{
+ GskGLTextureAtlasEntry entry;
+ PangoRectangle ink_rect;
+} GskGLGlyphValue;
+
#if GLIB_SIZEOF_VOID_P == 8
G_STATIC_ASSERT (sizeof (GskGLGlyphKey) == 16);
#elif GLIB_SIZEOF_VOID_P == 4
G_STATIC_ASSERT (sizeof (GskGLGlyphKey) == 12);
#endif
-typedef struct _GskGLGlyphValue
-{
- GskGLTextureAtlas *atlas;
- guint texture_id;
-
- float tx;
- float ty;
- float tw;
- float th;
-
- int draw_x;
- int draw_y;
- int draw_width;
- int draw_height;
-
- guint accessed : 1; /* accessed since last check */
- guint used : 1; /* accounted as used in the atlas */
-} GskGLGlyphValue;
-
-#define GSK_TYPE_GL_GLYPH_LIBRARY (gsk_gl_glyph_library_get_type())
-
G_DECLARE_FINAL_TYPE (GskGLGlyphLibrary, gsk_gl_glyph_library, GSK, GL_GLYPH_LIBRARY, GskGLTextureLibrary)
struct _GskGLGlyphLibrary
@@ -71,7 +58,7 @@ struct _GskGLGlyphLibrary
GHashTable *hash_table;
};
-GskGLGlyphLibrary *gsk_gl_glyph_library_new (GdkGLContext *context);
+GskGLGlyphLibrary *gsk_gl_glyph_library_new (GskNextDriver *driver);
gboolean gsk_gl_glyph_library_add (GskGLGlyphLibrary *self,
const GskGLGlyphKey *key,
const GskGLGlyphValue **out_value);
@@ -98,32 +85,12 @@ gsk_gl_glyph_library_lookup_or_add (GskGLGlyphLibrary *self,
const GskGLGlyphKey *key,
const GskGLGlyphValue **out_value)
{
- GskGLGlyphValue *value = g_hash_table_lookup (self->hash_table, key);
-
- /* Optimize for the fast path (repeated lookups of a character */
- if G_LIKELY (value && value->accessed && value->used)
- {
- *out_value = value;
- return value->texture_id > 0;
- }
+ GskGLTextureAtlasEntry *entry;
- /* We found it, but haven't marked as used for this frame */
- if (value != NULL)
+ if G_LIKELY (gsk_gl_texture_library_lookup ((GskGLTextureLibrary *)self, key, &entry))
{
- value->accessed = TRUE;
-
- if (!value->used)
- {
- gsk_gl_texture_library_mark_used (self,
- value->atlas,
- value->draw_width,
- value->draw_height);
- value->used = TRUE;
- }
-
- *out_value = value;
-
- return value->texture_id > 0;
+ *out_value = (GskGLGlyphValue *)entry;
+ return TRUE;
}
return gsk_gl_glyph_library_add (self, key, out_value);
diff --git a/gsk/next/gskgliconlibrary.c b/gsk/next/gskgliconlibrary.c
index 64e800d115..5cbdccb04a 100644
--- a/gsk/next/gskgliconlibrary.c
+++ b/gsk/next/gskgliconlibrary.c
@@ -20,6 +20,7 @@
#include "config.h"
+#include "gskgldriverprivate.h"
#include "gskgliconlibraryprivate.h"
struct _GskGLIconLibrary
@@ -30,12 +31,12 @@ struct _GskGLIconLibrary
G_DEFINE_TYPE (GskGLIconLibrary, gsk_gl_icon_library, GSK_TYPE_GL_TEXTURE_LIBRARY)
GskGLIconLibrary *
-gsk_gl_icon_library_new (GdkGLContext *context)
+gsk_gl_icon_library_new (GskNextDriver *driver)
{
- g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
+ g_return_val_if_fail (GSK_IS_NEXT_DRIVER (driver), NULL);
return g_object_new (GSK_TYPE_GL_ICON_LIBRARY,
- "context", context,
+ "driver", driver,
NULL);
}
diff --git a/gsk/next/gskgliconlibraryprivate.h b/gsk/next/gskgliconlibraryprivate.h
index c6b4363e33..37812cc554 100644
--- a/gsk/next/gskgliconlibraryprivate.h
+++ b/gsk/next/gskgliconlibraryprivate.h
@@ -31,7 +31,7 @@ G_BEGIN_DECLS
G_DECLARE_FINAL_TYPE (GskGLIconLibrary, gsk_gl_icon_library, GSK, GL_ICON_LIBRARY, GskGLTextureLibrary)
-GskGLIconLibrary *gsk_gl_icon_library_new (GdkGLContext *context);
+GskGLIconLibrary *gsk_gl_icon_library_new (GskNextDriver *driver);
G_END_DECLS
diff --git a/gsk/next/gskglrenderjob.c b/gsk/next/gskglrenderjob.c
index 995b3c927a..9ba57d7afd 100644
--- a/gsk/next/gskglrenderjob.c
+++ b/gsk/next/gskglrenderjob.c
@@ -2524,6 +2524,7 @@ gsk_gl_render_job_visit_text_node (GskGLRenderJob *job,
float tx, ty, tx2, ty2;
float cx;
float cy;
+ guint texture_id;
if (gi->glyph == PANGO_GLYPH_EMPTY)
continue;
@@ -2536,27 +2537,27 @@ gsk_gl_render_job_visit_text_node (GskGLRenderJob *job,
if (!gsk_gl_glyph_library_lookup_or_add (library, &lookup, &glyph))
goto next;
- g_assert (glyph->texture_id != 0);
+ texture_id = GSK_GL_TEXTURE_ATLAS_ENTRY_TEXTURE (glyph);
- if (last_texture != glyph->texture_id)
+ if (last_texture != texture_id)
{
gsk_gl_program_set_uniform_texture (program,
UNIFORM_SHARED_SOURCE,
GL_TEXTURE_2D,
GL_TEXTURE0,
- glyph->texture_id);
- last_texture = glyph->texture_id;
+ texture_id);
+ last_texture = texture_id;
}
- tx = glyph->tx;
- ty = glyph->ty;
- tx2 = tx + glyph->tw;
- ty2 = ty + glyph->th;
+ tx = glyph->entry.area.origin.x;
+ ty = glyph->entry.area.origin.y;
+ tx2 = tx + glyph->entry.area.size.width;
+ ty2 = ty + glyph->entry.area.size.height;
- glyph_x = floor (x + cx + 0.125) + glyph->draw_x;
- glyph_y = floor (y + cy + 0.125) + glyph->draw_y;
- glyph_x2 = glyph_x + glyph->draw_width;
- glyph_y2 = glyph_y + glyph->draw_height;
+ glyph_x = floor (x + cx + 0.125) + glyph->ink_rect.x;
+ glyph_y = floor (y + cy + 0.125) + glyph->ink_rect.y;
+ glyph_x2 = glyph_x + glyph->ink_rect.width;
+ glyph_y2 = glyph_y + glyph->ink_rect.height;
vertices = gsk_gl_command_queue_add_vertices (job->command_queue, NULL);
diff --git a/gsk/next/gskglshadowlibrary.c b/gsk/next/gskglshadowlibrary.c
index af611adba7..dbcba6f6ed 100644
--- a/gsk/next/gskglshadowlibrary.c
+++ b/gsk/next/gskglshadowlibrary.c
@@ -20,6 +20,7 @@
#include "config.h"
+#include "gskgldriverprivate.h"
#include "gskglshadowlibraryprivate.h"
struct _GskGLShadowLibrary
@@ -30,12 +31,12 @@ struct _GskGLShadowLibrary
G_DEFINE_TYPE (GskGLShadowLibrary, gsk_gl_shadow_library, GSK_TYPE_GL_TEXTURE_LIBRARY)
GskGLShadowLibrary *
-gsk_gl_shadow_library_new (GdkGLContext *context)
+gsk_gl_shadow_library_new (GskNextDriver *driver)
{
- g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
+ g_return_val_if_fail (GSK_IS_NEXT_DRIVER (driver), NULL);
return g_object_new (GSK_TYPE_GL_SHADOW_LIBRARY,
- "context", context,
+ "driver", driver,
NULL);
}
diff --git a/gsk/next/gskglshadowlibraryprivate.h b/gsk/next/gskglshadowlibraryprivate.h
index 3493e6d93d..57f952395c 100644
--- a/gsk/next/gskglshadowlibraryprivate.h
+++ b/gsk/next/gskglshadowlibraryprivate.h
@@ -35,7 +35,7 @@ typedef struct _GskGLShadowKey
G_DECLARE_FINAL_TYPE (GskGLShadowLibrary, gsk_gl_shadow_library, GSK, GL_SHADOW_LIBRARY, GskGLTextureLibrary)
-GskGLShadowLibrary *gsk_gl_shadow_library_new (GdkGLContext *context);
+GskGLShadowLibrary *gsk_gl_shadow_library_new (GskNextDriver *driver);
G_END_DECLS
diff --git a/gsk/next/gskgltextureatlas.c b/gsk/next/gskgltextureatlas.c
index d0b98e89bc..1456c77fe3 100644
--- a/gsk/next/gskgltextureatlas.c
+++ b/gsk/next/gskgltextureatlas.c
@@ -1,46 +1,238 @@
-/* gskgltextureatlas.c
- *
- * Copyright 2020 Christian Hergert <chergert redhat com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * SPDX-License-Identifier: LGPL-2.1-or-later
- */
-
#include "config.h"
+#include <epoxy/gl.h>
+
#include "gskgltextureatlasprivate.h"
+#include "gskdebugprivate.h"
+#include "gdkglcontextprivate.h"
+
+#define ATLAS_SIZE 512
+#define MAX_OLD_RATIO 0.5
-struct _GskGLTextureAtlas
+void
+gsk_gl_texture_atlas_free (GskGLTextureAtlas *atlas)
{
- GObject parent_instance;
-};
+ if (atlas->texture_id != 0)
+ {
+ glDeleteTextures (1, &atlas->texture_id);
+ atlas->texture_id = 0;
+ }
-G_DEFINE_TYPE (GskGLTextureAtlas, gsk_gl_texture_atlas, G_TYPE_OBJECT)
+ g_clear_pointer (&atlas->nodes, g_free);
+ g_slice_free (GskGLTextureAtlas, atlas);
+}
-GskGLTextureAtlas *
-gsk_gl_texture_atlas_new (void)
+GskGLTextureAtlases *
+gsk_gl_texture_atlases_new (void)
{
- return g_object_new (GSK_TYPE_GL_TEXTURE_ATLAS, NULL);
+ return g_ptr_array_new_with_free_func ((GDestroyNotify)gsk_gl_texture_atlas_free);
}
+#if 0
static void
-gsk_gl_texture_atlas_class_init (GskGLTextureAtlasClass *klass)
+write_atlas_to_png (GskGLTextureAtlas *atlas,
+ const char *filename)
{
+ int stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, atlas->width);
+ guchar *data = g_malloc (atlas->height * stride);
+ cairo_surface_t *s;
+
+ 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);
}
+#endif
-static void
-gsk_gl_texture_atlas_init (GskGLTextureAtlas *self)
+void
+gsk_gl_texture_atlases_begin_frame (GskGLTextureAtlases *self,
+ GPtrArray *removed)
+{
+ g_return_if_fail (self != NULL);
+
+ for (int i = self->len - 1; i >= 0; i--)
+ {
+ GskGLTextureAtlas *atlas = g_ptr_array_index (self, i);
+
+ if (gsk_gl_texture_atlas_get_unused_ratio (atlas) > MAX_OLD_RATIO)
+ {
+ if (atlas->texture_id != 0)
+ {
+ glDeleteTextures (1, &atlas->texture_id);
+ atlas->texture_id = 0;
+ }
+
+ g_ptr_array_add (removed, atlas);
+ g_ptr_array_remove_index (self, i);
+ }
+ }
+
+#if 0
+ {
+ static guint timestamp;
+
+ timestamp++;
+ if (timestamp % 10 == 0)
+ for (i = 0; i < self->len; i++)
+ {
+ GskGLTextureAtlas *atlas = g_ptr_array_index (self, i);
+
+ if (atlas->texture_id)
+ {
+ char *filename;
+
+ filename = g_strdup_printf ("textureatlas%d-%u.png", i, timestamp);
+ write_atlas_to_png (atlas, filename);
+ g_free (filename);
+ }
+ }
+ }
+#endif
+}
+
+gboolean
+gsk_gl_texture_atlases_pack (GskGLTextureAtlases *self,
+ int width,
+ int height,
+ GskGLTextureAtlas **atlas_out,
+ int *out_x,
+ int *out_y)
+{
+ GskGLTextureAtlas *atlas;
+ int x, y;
+
+ g_assert (width < ATLAS_SIZE);
+ g_assert (height < ATLAS_SIZE);
+
+ atlas = NULL;
+
+ for (guint i = 0; i < self->len; i++)
+ {
+ atlas = g_ptr_array_index (self, i);
+
+ if (gsk_gl_texture_atlas_pack (atlas, width, height, &x, &y))
+ break;
+
+ atlas = NULL;
+ }
+
+ if (atlas == NULL)
+ {
+ /* 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_texture_atlas_realize (atlas);
+ g_ptr_array_add (self, atlas);
+
+ /* Pack it onto that one, which surely has enough space... */
+ if (!gsk_gl_texture_atlas_pack (atlas, width, height, &x, &y))
+ g_assert_not_reached ();
+ }
+
+ *atlas_out = atlas;
+ *out_x = x;
+ *out_y = y;
+
+ return TRUE;
+}
+
+void
+gsk_gl_texture_atlas_init (GskGLTextureAtlas *self,
+ int width,
+ int height)
{
+ memset (self, 0, sizeof (*self));
+
+ self->texture_id = 0;
+ self->width = width;
+ self->height = height;
+
+ /* TODO: We might want to change the strategy about the amount of
+ * nodes here? stb_rect_pack.h says with is optimal. */
+ self->nodes = g_malloc0 (sizeof (struct stbrp_node) * width);
+ stbrp_init_target (&self->context,
+ width, height,
+ self->nodes,
+ width);
+
+ gsk_gl_texture_atlas_realize (self);
+}
+
+gboolean
+gsk_gl_texture_atlas_pack (GskGLTextureAtlas *self,
+ int width,
+ int height,
+ int *out_x,
+ int *out_y)
+{
+ stbrp_rect rect;
+
+ g_assert (out_x);
+ g_assert (out_y);
+
+ 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;
+}
+
+double
+gsk_gl_texture_atlas_get_unused_ratio (const GskGLTextureAtlas *self)
+{
+ if (self->unused_pixels > 0)
+ return (double)(self->unused_pixels) / (double)(self->width * self->height);
+
+ return 0.0;
+}
+
+/* Not using gdk_gl_driver_create_texture here, since we want
+ * this texture to survive the driver and stay around until
+ * the display gets closed.
+ */
+static guint
+create_shared_texture (int width,
+ int height)
+{
+ guint texture_id;
+
+ glGenTextures (1, &texture_id);
+ glBindTexture (GL_TEXTURE_2D, texture_id);
+
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ if (gdk_gl_context_get_use_es (gdk_gl_context_get_current ()))
+ glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+ else
+ glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
+
+ glBindTexture (GL_TEXTURE_2D, 0);
+
+ return texture_id;
+}
+
+void
+gsk_gl_texture_atlas_realize (GskGLTextureAtlas *atlas)
+{
+ if (atlas->texture_id)
+ return;
+
+ atlas->texture_id = create_shared_texture (atlas->width, atlas->height);
+ gdk_gl_context_label_object_printf (gdk_gl_context_get_current (),
+ GL_TEXTURE, atlas->texture_id,
+ "Texture atlas %d", atlas->texture_id);
}
diff --git a/gsk/next/gskgltextureatlasprivate.h b/gsk/next/gskgltextureatlasprivate.h
index ef840d30d3..289363ef7f 100644
--- a/gsk/next/gskgltextureatlasprivate.h
+++ b/gsk/next/gskgltextureatlasprivate.h
@@ -1,35 +1,35 @@
-/* gskgltextureatlasprivate.h
- *
- * Copyright 2020 Christian Hergert <chergert redhat com>
- *
- * This file is free software; you can redistribute it and/or modify it under
- * the terms of the GNU Lesser General Public License as published by the Free
- * Software Foundation; either version 2.1 of the License, or (at your option)
- * any later version.
- *
- * This file is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
- * License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * SPDX-License-Identifier: LGPL-2.1-or-later
- */
-
-#ifndef __GSK_GL_TEXTURE_ATLAS_PRIVATE_H__
-#define __GSK_GL_TEXTURE_ATLAS_PRIVATE_H__
-
-#include "gskgltypesprivate.h"
+#ifndef __GSK_GL_TEXTURE_ATLAS_H__
+#define __GSK_GL_TEXTURE_ATLAS_H__
-G_BEGIN_DECLS
+#include "../gl/stb_rect_pack.h"
+#include "gskgldriverprivate.h"
-#define GSK_TYPE_GL_TEXTURE_ATLAS (gsk_gl_texture_atlas_get_type())
+G_BEGIN_DECLS
-G_DECLARE_FINAL_TYPE (GskGLTextureAtlas, gsk_gl_texture_atlas, GSK, GL_TEXTURE_ATLAS, GObject)
+typedef GPtrArray GskGLTextureAtlases;
+
+
+GskGLTextureAtlases *gsk_gl_texture_atlases_new (void);
+void gsk_gl_texture_atlases_begin_frame (GskGLTextureAtlases *atlases,
+ GPtrArray *removed);
+gboolean gsk_gl_texture_atlases_pack (GskGLTextureAtlases *atlases,
+ int width,
+ int height,
+ GskGLTextureAtlas **atlas_out,
+ int *out_x,
+ int *out_y);
+void gsk_gl_texture_atlas_init (GskGLTextureAtlas *self,
+ int width,
+ int height);
+void gsk_gl_texture_atlas_free (GskGLTextureAtlas *self);
+void gsk_gl_texture_atlas_realize (GskGLTextureAtlas *self);
+gboolean gsk_gl_texture_atlas_pack (GskGLTextureAtlas *self,
+ int width,
+ int height,
+ int *out_x,
+ int *out_y);
+double gsk_gl_texture_atlas_get_unused_ratio (const GskGLTextureAtlas *self);
-GskGLTextureAtlas *gsk_gl_texture_atlas_new (void);
G_END_DECLS
diff --git a/gsk/next/gskgltexturelibrary.c b/gsk/next/gskgltexturelibrary.c
index 2847c48e5a..c4ef5e849d 100644
--- a/gsk/next/gskgltexturelibrary.c
+++ b/gsk/next/gskgltexturelibrary.c
@@ -20,40 +20,34 @@
#include "config.h"
-#include "gskgltextureatlasprivate.h"
+#include "gskglcommandqueueprivate.h"
+#include "gskgldriverprivate.h"
#include "gskgltexturelibraryprivate.h"
-typedef struct
-{
- guint hash;
- guint len;
- guint8 key[0];
-} Entry;
-
-typedef struct
-{
- GdkGLContext *context;
- GHashFunc hash_func;
- GEqualFunc equal_func;
-} GskGLTextureLibraryPrivate;
-
-G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GskGLTextureLibrary, gsk_gl_texture_library, G_TYPE_OBJECT)
+G_DEFINE_ABSTRACT_TYPE (GskGLTextureLibrary, gsk_gl_texture_library, G_TYPE_OBJECT)
enum {
PROP_0,
- PROP_CONTEXT,
+ PROP_DRIVER,
N_PROPS
};
static GParamSpec *properties [N_PROPS];
+static void
+gsk_gl_texture_library_constructed (GObject *object)
+{
+ G_OBJECT_CLASS (gsk_gl_texture_library_parent_class)->constructed (object);
+
+ g_assert (GSK_GL_TEXTURE_LIBRARY (object)->hash_table != NULL);
+}
+
static void
gsk_gl_texture_library_dispose (GObject *object)
{
GskGLTextureLibrary *self = (GskGLTextureLibrary *)object;
- GskGLTextureLibraryPrivate *priv = gsk_gl_texture_library_get_instance_private (self);
- g_clear_object (&priv->context);
+ g_clear_object (&self->driver);
G_OBJECT_CLASS (gsk_gl_texture_library_parent_class)->dispose (object);
}
@@ -65,12 +59,11 @@ gsk_gl_texture_library_get_property (GObject *object,
GParamSpec *pspec)
{
GskGLTextureLibrary *self = GSK_GL_TEXTURE_LIBRARY (object);
- GskGLTextureLibraryPrivate *priv = gsk_gl_texture_library_get_instance_private (self);
switch (prop_id)
{
- case PROP_CONTEXT:
- g_value_set_object (value, priv->context);
+ case PROP_DRIVER:
+ g_value_set_object (value, self->driver);
break;
default:
@@ -85,12 +78,11 @@ gsk_gl_texture_library_set_property (GObject *object,
GParamSpec *pspec)
{
GskGLTextureLibrary *self = GSK_GL_TEXTURE_LIBRARY (object);
- GskGLTextureLibraryPrivate *priv = gsk_gl_texture_library_get_instance_private (self);
switch (prop_id)
{
- case PROP_CONTEXT:
- priv->context = g_value_dup_object (value);
+ case PROP_DRIVER:
+ self->driver = g_value_dup_object (value);
break;
default:
@@ -103,15 +95,16 @@ gsk_gl_texture_library_class_init (GskGLTextureLibraryClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ object_class->constructed = gsk_gl_texture_library_constructed;
object_class->dispose = gsk_gl_texture_library_dispose;
object_class->get_property = gsk_gl_texture_library_get_property;
object_class->set_property = gsk_gl_texture_library_set_property;
- properties [PROP_CONTEXT] =
- g_param_spec_object ("context",
- "Context",
- "Context",
- GDK_TYPE_GL_CONTEXT,
+ properties [PROP_DRIVER] =
+ g_param_spec_object ("driver",
+ "Driver",
+ "Driver",
+ GSK_TYPE_NEXT_DRIVER,
(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
@@ -120,21 +113,20 @@ gsk_gl_texture_library_class_init (GskGLTextureLibraryClass *klass)
static void
gsk_gl_texture_library_init (GskGLTextureLibrary *self)
{
- GskGLTextureLibraryPrivate *priv = gsk_gl_texture_library_get_instance_private (self);
-
- priv->hash_func = g_direct_hash;
- priv->equal_func = g_direct_equal;
}
-GdkGLContext *
-gsk_gl_texture_library_get_context (GskGLTextureLibrary *self)
+void
+gsk_gl_texture_library_set_funcs (GskGLTextureLibrary *self,
+ GHashFunc hash_func,
+ GEqualFunc equal_func,
+ GDestroyNotify key_destroy,
+ GDestroyNotify value_destroy)
{
- GskGLTextureLibraryPrivate *priv = gsk_gl_texture_library_get_instance_private (self);
-
- g_return_val_if_fail (GSK_IS_GL_TEXTURE_LIBRARY (self), NULL);
- g_return_val_if_fail (GDK_IS_GL_CONTEXT (priv->context), NULL);
+ g_return_if_fail (GSK_IS_GL_TEXTURE_LIBRARY (self));
+ g_return_if_fail (self->hash_table == NULL);
- return priv->context;
+ self->hash_table = g_hash_table_new_full (hash_func, equal_func,
+ key_destroy, value_destroy);
}
void
@@ -155,25 +147,145 @@ gsk_gl_texture_library_end_frame (GskGLTextureLibrary *self)
GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->end_frame (self);
}
-gboolean
-gsk_gl_texture_library_pack (GskGLTextureLibrary *self,
- gconstpointer key,
- gsize keylen,
- float width,
- float height,
- GskGLTextureAtlas **atlas,
- graphene_rect_t *area)
+static GskGLTexture *
+gsk_gl_texture_library_pack_one (GskGLTextureLibrary *self,
+ guint width,
+ guint height)
{
- return FALSE;
+ GskGLTexture *texture;
+
+ g_assert (GSK_IS_GL_TEXTURE_LIBRARY (self));
+
+ if (width > self->driver->command_queue->max_texture_size ||
+ height > self->driver->command_queue->max_texture_size)
+ {
+ g_warning ("Clipping requested texture of size %ux%u to maximum allowable size %u.",
+ width, height, self->driver->command_queue->max_texture_size);
+ width = MIN (width, self->driver->command_queue->max_texture_size);
+ height = MIN (height, self->driver->command_queue->max_texture_size);
+ }
+
+ texture = gsk_next_driver_create_texture (self->driver, width, height, GL_LINEAR, GL_LINEAR);
+ texture->permanent = TRUE;
+
+ return texture;
}
-gboolean
-gsk_gl_texture_library_lookup (GskGLTextureLibrary *library,
- gconstpointer key,
- GskGLTextureAtlas **atlas,
- graphene_rect_t *area)
+static inline gboolean
+gsk_gl_texture_atlas_pack (GskGLTextureAtlas *self,
+ int width,
+ int height,
+ int *out_x,
+ int *out_y)
{
- return FALSE;
+ 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_atlases_pack (GskNextDriver *driver,
+ 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);
+
+ if (gsk_gl_texture_atlas_pack (atlas, width, height, &x, &y))
+ break;
+
+ atlas = NULL;
+ }
+
+ if (atlas == NULL)
+ {
+ /* No atlas has enough space, so create a new one... */
+ atlas = gsk_next_driver_create_atlas (driver);
+
+ /* Pack it onto that one, which surely has enough space... */
+ if (!gsk_gl_texture_atlas_pack (atlas, width, height, &x, &y))
+ g_assert_not_reached ();
+ }
+
+ *out_atlas = atlas;
+ *out_x = x;
+ *out_y = y;
+}
+
+gpointer
+gsk_gl_texture_library_pack (GskGLTextureLibrary *self,
+ gconstpointer key,
+ gsize keylen,
+ gsize valuelen,
+ guint width,
+ guint height)
+{
+ GskGLTextureAtlasEntry *entry;
+ GskGLTextureAtlas *atlas = NULL;
+
+ g_assert (GSK_IS_GL_TEXTURE_LIBRARY (self));
+ g_assert (key != NULL);
+ g_assert (keylen > 0);
+ g_assert (valuelen > sizeof (GskGLTextureAtlasEntry));
+
+ entry = g_slice_alloc0 (valuelen);
+ entry->n_pixels = width * height;
+ entry->accessed = TRUE;
+
+ if (width <= self->max_entry_size && height <= self->max_entry_size)
+ {
+ int packed_x;
+ int packed_y;
+
+ gsk_gl_texture_atlases_pack (self->driver,
+ width + 2,
+ height + 2,
+ &atlas,
+ &packed_x,
+ &packed_y);
+
+ entry->atlas = atlas;
+ entry->is_atlased = TRUE;
+ entry->area.origin.x = (float)(packed_x + 1) / atlas->width;
+ entry->area.origin.y = (float)(packed_y + 1) / atlas->height;
+ entry->area.size.width = width / atlas->width;
+ entry->area.size.height = height / atlas->height;
+ }
+ else
+ {
+ GskGLTexture *texture = gsk_gl_texture_library_pack_one (self, width + 2, height + 2);
+
+ entry->texture = texture;
+ entry->is_atlased = FALSE;
+ entry->area.origin.x = 0.0f;
+ entry->area.origin.y = 0.0f;
+ entry->area.size.width = 1.0f;
+ entry->area.size.height = 1.0f;
+ }
+
+ g_hash_table_insert (self->hash_table,
+ g_slice_copy (keylen, key),
+ entry);
+
+ return entry;
}
void
diff --git a/gsk/next/gskgltexturelibraryprivate.h b/gsk/next/gskgltexturelibraryprivate.h
index 36e7a8d506..491f87b6b8 100644
--- a/gsk/next/gskgltexturelibraryprivate.h
+++ b/gsk/next/gskgltexturelibraryprivate.h
@@ -22,46 +22,166 @@
#define __GSK_GL_TEXTURE_LIBRARY_PRIVATE_H__
#include "gskgltypesprivate.h"
+#include "gskgltexturepoolprivate.h"
+#include "../gl/stb_rect_pack.h"
G_BEGIN_DECLS
-#define GSK_TYPE_GL_TEXTURE_LIBRARY (gsk_gl_texture_library_get_type())
+#define GSK_TYPE_GL_TEXTURE_LIBRARY (gsk_gl_texture_library_get_type ())
+#define GSK_GL_TEXTURE_LIBRARY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),
GSK_TYPE_GL_TEXTURE_LIBRARY, GskGLTextureLibrary))
+#define GSK_IS_GL_TEXTURE_LIBRARY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),
GSK_TYPE_GL_TEXTURE_LIBRARY))
+#define GSK_GL_TEXTURE_LIBRARY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),
GSK_TYPE_GL_TEXTURE_LIBRARY, GskGLTextureLibraryClass))
+#define GSK_IS_GL_TEXTURE_LIBRARY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),
GSK_TYPE_GL_TEXTURE_LIBRARY))
+#define GSK_GL_TEXTURE_LIBRARY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),
GSK_TYPE_GL_TEXTURE_LIBRARY, GskGLTextureLibraryClass))
-G_DECLARE_DERIVABLE_TYPE (GskGLTextureLibrary, gsk_gl_texture_library, GSK, GL_TEXTURE_LIBRARY, GObject)
-
-struct _GskGLTextureAtlas
+typedef struct _GskGLTextureAtlas
{
+ struct stbrp_context context;
+ struct stbrp_node *nodes;
+
+ int width;
+ int height;
+
guint texture_id;
-};
-struct _GskGLTextureLibraryClass
+ /* Pixels of rects that have been used at some point,
+ * But are now unused.
+ */
+ int unused_pixels;
+
+ void *user_data;
+} GskGLTextureAtlas;
+
+typedef struct _GskGLTextureAtlasEntry
+{
+ /* A backreference to either the atlas or texture containing
+ * the contents of the atlas entry. For larger items, no atlas
+ * is used and instead a direct texture.
+ */
+ union {
+ GskGLTextureAtlas *atlas;
+ GskGLTexture *texture;
+ };
+
+ /* The area within the atlas translated to 0..1 bounds */
+ graphene_rect_t area;
+
+ /* Number of pixels in the entry, used to calculate usage
+ * of an atlas while processing.
+ */
+ guint n_pixels : 29;
+
+ /* If entry has marked pixels as used in the atlas this frame */
+ guint used : 1;
+
+ /* If entry was accessed this frame */
+ guint accessed : 1;
+
+ /* When true, backref is an atlas, otherwise texture */
+ guint is_atlased : 1;
+
+ /* Suffix data that is per-library specific. gpointer used to
+ * guarantee the alignment for the entries using this.
+ */
+ gpointer data[0];
+} GskGLTextureAtlasEntry;
+
+typedef struct _GskGLTextureLibrary
+{
+ GObject parent_instance;
+ GskNextDriver *driver;
+ GHashTable *hash_table;
+ guint max_entry_size;
+} GskGLTextureLibrary;
+
+typedef struct _GskGLTextureLibraryClass
{
GObjectClass parent_class;
void (*begin_frame) (GskGLTextureLibrary *library);
void (*end_frame) (GskGLTextureLibrary *library);
-};
-
-GdkGLContext *gsk_gl_texture_library_get_context (GskGLTextureLibrary *self);
-void gsk_gl_texture_library_set_funcs (GHashFunc hash_func,
- GEqualFunc equal_func);
-void gsk_gl_texture_library_begin_frame (GskGLTextureLibrary *self);
-void gsk_gl_texture_library_end_frame (GskGLTextureLibrary *self);
-gboolean gsk_gl_texture_library_pack (GskGLTextureLibrary *self,
- gconstpointer key,
- gsize keylen,
- float width,
- float height,
- GskGLTextureAtlas **atlas,
- graphene_rect_t *area);
-gboolean gsk_gl_texture_library_lookup (GskGLTextureLibrary *library,
- gconstpointer key,
- GskGLTextureAtlas **atlas,
- graphene_rect_t *area);
-void gsk_gl_texture_library_upload (GskGLTextureLibrary *self,
- GskGLTextureAtlas *atlas,
- const graphene_rect_t *area,
- GdkTexture *texture);
+} 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);
+void gsk_gl_texture_library_end_frame (GskGLTextureLibrary *self);
+gpointer gsk_gl_texture_library_pack (GskGLTextureLibrary *self,
+ gconstpointer key,
+ gsize keylen,
+ gsize valuelen,
+ guint width,
+ guint height);
+void gsk_gl_texture_library_upload (GskGLTextureLibrary *self,
+ GskGLTextureAtlas *atlas,
+ const graphene_rect_t *area,
+ GdkTexture *texture);
+
+static inline void
+gsk_gl_texture_atlas_mark_unused (GskGLTextureAtlas *self,
+ int n_pixels)
+{
+ self->unused_pixels += n_pixels;
+}
+
+static inline void
+gsk_gl_texture_atlas_mark_used (GskGLTextureAtlas *self,
+ int n_pixels)
+{
+ self->unused_pixels -= n_pixels;
+}
+
+static inline gboolean
+gsk_gl_texture_library_lookup (GskGLTextureLibrary *self,
+ gconstpointer key,
+ GskGLTextureAtlasEntry **out_entry)
+{
+ GskGLTextureAtlasEntry *entry = g_hash_table_lookup (self->hash_table, key);
+
+ if G_LIKELY (entry != NULL && entry->accessed && entry->used)
+ {
+ *out_entry = entry;
+ return TRUE;
+ }
+
+ if (entry != NULL)
+ {
+ if (!entry->used && entry->is_atlased)
+ {
+ g_assert (entry->atlas != NULL);
+ gsk_gl_texture_atlas_mark_used (entry->atlas, entry->n_pixels);
+ entry->used = TRUE;
+ }
+
+ entry->accessed = TRUE;
+ *out_entry = entry;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static inline guint
+GSK_GL_TEXTURE_ATLAS_ENTRY_TEXTURE (gconstpointer d)
+{
+ const GskGLTextureAtlasEntry *e = d;
+
+ return e->is_atlased ? e->atlas->texture_id : e->texture->texture_id;
+}
+
+static inline double
+gsk_gl_texture_atlas_get_unused_ratio (const GskGLTextureAtlas *self)
+{
+ if (self->unused_pixels > 0)
+ return (double)(self->unused_pixels) / (double)(self->width * self->height);
+ return 0.0;
+}
G_END_DECLS
diff --git a/gsk/next/gskgltypesprivate.h b/gsk/next/gskgltypesprivate.h
index 03aa07383a..efb2c4c500 100644
--- a/gsk/next/gskgltypesprivate.h
+++ b/gsk/next/gskgltypesprivate.h
@@ -41,6 +41,7 @@ typedef struct _GskGLIconLibrary GskGLIconLibrary;
typedef struct _GskGLProgram GskGLProgram;
typedef struct _GskGLRenderJob GskGLRenderJob;
typedef struct _GskGLShadowLibrary GskGLShadowLibrary;
+typedef struct _GskGLTexture GskGLTexture;
typedef struct _GskGLTextureAtlas GskGLTextureAtlas;
typedef struct _GskGLTextureLibrary GskGLTextureLibrary;
typedef struct _GskGLUniformState GskGLUniformState;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]