gtk2/gsk/gl/gskgltexturelibraryprivate.h
Christian Hergert c64836e1c9 gsk/gl: make texture libraries more autonomous
This moves a lot of the texture atlas control out of the driver and into
the various texture libraries through their base GskGLTextureLibrary class.

Additionally, this gives more control to libraries on allocating which can
be necessary for some tooling such as future Glyphy integration.

As part of this, the 1x1 pixel initialization is moved to the Glyph library
which is the only place where it is actually needed.

The compact vfunc now is responsible for compaction and it allows for us
to iterate the atlas hashtable a single time instead of twice as we were
doing previously.

The init_atlas vfunc is used to do per-library initialization such as
adding a 1x1 pixel in the Glyph cache used for coloring lines.

The allocate vfunc purely allocates but does no upload. This can be useful
for situations where a library wants to reuse the allocator from the
base class but does not want to actually insert a key/value entry. The
glyph library uses this for it's 1x1 pixel.

In the future, we will also likely want to decouple the rectangle packing
implementation from the atlas structure, or at least move it into a union
so that we do not allocate unused memory for alternate allocators.
2022-03-18 14:59:46 -07:00

235 lines
8.5 KiB
C

/* gskgltexturelibraryprivate.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_LIBRARY_PRIVATE_H__
#define __GSK_GL_TEXTURE_LIBRARY_PRIVATE_H__
#include "gskgltypesprivate.h"
#include "gskgltextureprivate.h"
#include "stb_rect_pack.h"
G_BEGIN_DECLS
#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))
typedef struct _GskGLTextureAtlas
{
struct stbrp_context context;
struct stbrp_node *nodes;
int width;
int height;
guint texture_id;
/* Pixels of rects that have been used at some point,
* But are now unused.
*/
int unused_pixels;
} 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 */
struct {
float x;
float y;
float x2;
float y2;
} 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;
} GskGLTextureAtlasEntry;
typedef struct _GskGLTextureLibrary
{
GObject parent_instance;
GskGLDriver *driver;
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);
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;
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,
int n_pixels)
{
g_assert (n_pixels >= 0);
self->unused_pixels += n_pixels;
}
static inline void
gsk_gl_texture_atlas_entry_mark_used (GskGLTextureAtlasEntry *entry)
{
if (entry->used == TRUE || entry->is_atlased == FALSE)
return;
entry->atlas->unused_pixels -= entry->n_pixels;
entry->used = TRUE;
}
static inline void
gsk_gl_texture_atlas_entry_mark_unused (GskGLTextureAtlasEntry *entry)
{
if (entry->used == FALSE || entry->is_atlased == FALSE)
return;
entry->atlas->unused_pixels += entry->n_pixels;
entry->used = FALSE;
}
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)
{
gsk_gl_texture_atlas_entry_mark_used (entry);
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 ? e->texture->texture_id : 0;
}
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;
}
static inline gboolean
gsk_gl_texture_library_can_cache (GskGLTextureLibrary *self,
int width,
int height)
{
g_assert (self->max_entry_size > 0);
return width <= self->max_entry_size && height <= self->max_entry_size;
}
G_END_DECLS
#endif /* __GSK_GL_TEXTURE_LIBRARY_PRIVATE_H__ */