gpu: Add a texture cache

... and use it when drawing textures.

We'll need it in other places later, but for now that's what we have.
This commit is contained in:
Benjamin Otte 2023-09-01 12:11:06 +02:00
parent 57c1c95e75
commit 373a6ab9f1
4 changed files with 323 additions and 1 deletions

View File

@ -3,16 +3,131 @@
#include "gskgpudeviceprivate.h"
#include "gdk/gdkdisplayprivate.h"
#include "gdk/gdktextureprivate.h"
typedef enum
{
GSK_GPU_CACHE_TEXTURE
} GskGpuCacheType;
typedef struct _GskGpuCachedTexture GskGpuCachedTexture;
typedef struct _GskGpuCacheEntry GskGpuCacheEntry;
struct _GskGpuCacheEntry
{
GskGpuCacheType type;
guint64 last_use_timestamp;
};
struct _GskGpuCachedTexture
{
GskGpuCacheEntry entry;
/* atomic */ GdkTexture *texture;
GskGpuImage *image;
};
#define GDK_ARRAY_NAME gsk_gpu_cache_entries
#define GDK_ARRAY_TYPE_NAME GskGpuCacheEntries
#define GDK_ARRAY_ELEMENT_TYPE GskGpuCacheEntry *
#define GDK_ARRAY_PREALLOC 32
#define GDK_ARRAY_NO_MEMSET 1
#include "gdk/gdkarrayimpl.c"
typedef struct _GskGpuDevicePrivate GskGpuDevicePrivate;
struct _GskGpuDevicePrivate
{
GdkDisplay *display;
GskGpuCacheEntries cache;
guint cache_gc_source;
};
G_DEFINE_TYPE_WITH_PRIVATE (GskGpuDevice, gsk_gpu_device, G_TYPE_OBJECT)
void
gsk_gpu_device_gc (GskGpuDevice *self,
gint64 timestamp)
{
GskGpuDevicePrivate *priv = gsk_gpu_device_get_instance_private (self);
gsize i;
for (i = 0; i < gsk_gpu_cache_entries_get_size (&priv->cache); i++)
{
GskGpuCacheEntry *entry = gsk_gpu_cache_entries_get (&priv->cache, i);
switch (entry->type)
{
case GSK_GPU_CACHE_TEXTURE:
{
GskGpuCachedTexture *texture = (GskGpuCachedTexture *) entry;
if (texture->texture != NULL)
continue;
gdk_texture_clear_render_data (texture->texture);
g_object_unref (texture->image);
}
break;
default:
g_assert_not_reached ();
break;
}
if (i + 1 != gsk_gpu_cache_entries_get_size (&priv->cache))
{
*gsk_gpu_cache_entries_index (&priv->cache, i) = *gsk_gpu_cache_entries_index (&priv->cache, gsk_gpu_cache_entries_get_size (&priv->cache) - 1);
gsk_gpu_cache_entries_set_size (&priv->cache, gsk_gpu_cache_entries_get_size (&priv->cache) - 1);
}
i--;
}
}
static void
gsk_gpu_device_clear_cache (GskGpuDevice *self)
{
GskGpuDevicePrivate *priv = gsk_gpu_device_get_instance_private (self);
gsize i;
for (i = 0; i < gsk_gpu_cache_entries_get_size (&priv->cache); i++)
{
GskGpuCacheEntry *entry = gsk_gpu_cache_entries_get (&priv->cache, i);
switch (entry->type)
{
case GSK_GPU_CACHE_TEXTURE:
{
GskGpuCachedTexture *texture = (GskGpuCachedTexture *) entry;
if (texture->texture)
gdk_texture_clear_render_data (texture->texture);
g_object_unref (texture->image);
}
break;
default:
g_assert_not_reached ();
break;
}
g_free (entry);
}
gsk_gpu_cache_entries_set_size (&priv->cache, 0);
}
static void
gsk_gpu_device_dispose (GObject *object)
{
GskGpuDevice *self = GSK_GPU_DEVICE (object);
GskGpuDevicePrivate *priv = gsk_gpu_device_get_instance_private (self);
gsk_gpu_device_clear_cache (self);
gsk_gpu_cache_entries_clear (&priv->cache);
G_OBJECT_CLASS (gsk_gpu_device_parent_class)->dispose (object);
}
static void
gsk_gpu_device_finalize (GObject *object)
{
@ -29,12 +144,16 @@ gsk_gpu_device_class_init (GskGpuDeviceClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = gsk_gpu_device_dispose;
object_class->finalize = gsk_gpu_device_finalize;
}
static void
gsk_gpu_device_init (GskGpuDevice *self)
{
GskGpuDevicePrivate *priv = gsk_gpu_device_get_instance_private (self);
gsk_gpu_cache_entries_init (&priv->cache);
}
void
@ -71,3 +190,54 @@ gsk_gpu_device_create_upload_image (GskGpuDevice *self,
{
return GSK_GPU_DEVICE_GET_CLASS (self)->create_upload_image (self, format, width, height);
}
GskGpuImage *
gsk_gpu_device_lookup_texture_image (GskGpuDevice *self,
GdkTexture *texture,
gint64 timestamp)
{
GskGpuCachedTexture *cache;
cache = gdk_texture_get_render_data (texture, self);
if (cache)
{
cache->entry.last_use_timestamp = timestamp;
return g_object_ref (cache->image);
}
return NULL;
}
static void
gsk_gpu_device_cache_texture_destroy_cb (gpointer data)
{
GskGpuCachedTexture *cache = data;
g_atomic_pointer_set (&cache->texture, NULL);
}
void
gsk_gpu_device_cache_texture_image (GskGpuDevice *self,
GdkTexture *texture,
gint64 timestamp,
GskGpuImage *image)
{
GskGpuDevicePrivate *priv = gsk_gpu_device_get_instance_private (self);
GskGpuCachedTexture *cache;
cache = g_new (GskGpuCachedTexture, 1);
*cache = (GskGpuCachedTexture) {
.entry = {
.type = GSK_GPU_CACHE_TEXTURE,
.last_use_timestamp = timestamp,
},
.texture = texture,
.image = g_object_ref (image)
};
if (gdk_texture_get_render_data (texture, self))
gdk_texture_clear_render_data (texture);
gdk_texture_set_render_data (texture, self, cache, gsk_gpu_device_cache_texture_destroy_cb);
gsk_gpu_cache_entries_append (&priv->cache, (GskGpuCacheEntry *) cache);
}

View File

@ -36,6 +36,8 @@ GType gsk_gpu_device_get_type (void) G
void gsk_gpu_device_setup (GskGpuDevice *self,
GdkDisplay *display);
void gsk_gpu_device_gc (GskGpuDevice *self,
gint64 timestamp);
GdkDisplay * gsk_gpu_device_get_display (GskGpuDevice *self);
@ -48,6 +50,14 @@ GskGpuImage * gsk_gpu_device_create_upload_image (GskGpuD
gsize width,
gsize height);
GskGpuImage * gsk_gpu_device_lookup_texture_image (GskGpuDevice *self,
GdkTexture *texture,
gint64 timestamp);
void gsk_gpu_device_cache_texture_image (GskGpuDevice *self,
GdkTexture *texture,
gint64 timestamp,
GskGpuImage *image);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskGpuDevice, g_object_unref)
G_END_DECLS

View File

@ -3,10 +3,12 @@
#include "gskgpunodeprocessorprivate.h"
#include "gskgpuclipprivate.h"
#include "gskgpudeviceprivate.h"
#include "gskgpuframeprivate.h"
#include "gskgpuglobalsopprivate.h"
#include "gskgpuimageprivate.h"
#include "gskgpupatternprivate.h"
#include "gskgpurenderpassopprivate.h"
#include "gskgpuscissoropprivate.h"
#include "gskgputextureopprivate.h"
#include "gskgpuuberopprivate.h"
@ -601,6 +603,38 @@ gsk_gpu_node_processor_add_color_node (GskGpuNodeProcessor *self,
pattern_id);
}
static void
gsk_gpu_node_processor_add_texture_node (GskGpuNodeProcessor *self,
GskRenderNode *node)
{
GskGpuDevice *device;
GskGpuImage *image;
GdkTexture *texture;
gint64 timestamp;
device = gsk_gpu_frame_get_device (self->frame);
texture = gsk_texture_node_get_texture (node);
timestamp = gsk_gpu_frame_get_timestamp (self->frame);
image = gsk_gpu_device_lookup_texture_image (device, texture, timestamp);
if (image == NULL)
{
image = gsk_gpu_upload_texture_op (self->frame, texture);
gsk_gpu_device_cache_texture_image (device, texture, timestamp, image);
image = g_object_ref (image);
}
gsk_gpu_texture_op (self->frame,
gsk_gpu_clip_get_shader_clip (&self->clip, &self->offset, &node->bounds),
image,
GSK_GPU_SAMPLER_DEFAULT,
&node->bounds,
&self->offset,
&node->bounds);
g_object_unref (image);
}
static void
gsk_gpu_node_processor_add_opacity_node (GskGpuNodeProcessor *self,
GskRenderNode *node)
@ -683,7 +717,7 @@ static const struct
},
[GSK_TEXTURE_NODE] = {
0,
NULL,
gsk_gpu_node_processor_add_texture_node,
},
[GSK_INSET_SHADOW_NODE] = {
0,

View File

@ -203,6 +203,114 @@ gsk_gpu_upload_op_vk_command (GskGpuOp *op,
}
#endif
typedef struct _GskGpuUploadTextureOp GskGpuUploadTextureOp;
struct _GskGpuUploadTextureOp
{
GskGpuOp op;
GskGpuImage *image;
GskGpuBuffer *buffer;
GdkTexture *texture;
};
static void
gsk_gpu_upload_texture_op_finish (GskGpuOp *op)
{
GskGpuUploadTextureOp *self = (GskGpuUploadTextureOp *) op;
g_object_unref (self->image);
g_clear_object (&self->buffer);
g_object_unref (self->texture);
}
static void
gsk_gpu_upload_texture_op_print (GskGpuOp *op,
GskGpuFrame *frame,
GString *string,
guint indent)
{
GskGpuUploadTextureOp *self = (GskGpuUploadTextureOp *) op;
gsk_gpu_print_op (string, indent, "upload-texture");
gsk_gpu_print_image (string, self->image);
gsk_gpu_print_newline (string);
}
static void
gsk_gpu_upload_texture_op_draw (GskGpuOp *op,
guchar *data,
gsize stride)
{
GskGpuUploadTextureOp *self = (GskGpuUploadTextureOp *) op;
GdkTextureDownloader *downloader;
downloader = gdk_texture_downloader_new (self->texture);
gdk_texture_downloader_set_format (downloader, gsk_gpu_image_get_format (self->image));
gdk_texture_downloader_download_into (downloader, data, stride);
gdk_texture_downloader_free (downloader);
}
#ifdef GDK_RENDERING_VULKAN
static GskGpuOp *
gsk_gpu_upload_texture_op_vk_command (GskGpuOp *op,
GskGpuFrame *frame,
VkRenderPass render_pass,
VkFormat format,
VkCommandBuffer command_buffer)
{
GskGpuUploadTextureOp *self = (GskGpuUploadTextureOp *) op;
return gsk_gpu_upload_op_vk_command (op,
frame,
command_buffer,
GSK_VULKAN_IMAGE (self->image),
gsk_gpu_upload_texture_op_draw,
&self->buffer);
}
#endif
static GskGpuOp *
gsk_gpu_upload_texture_op_gl_command (GskGpuOp *op,
GskGpuFrame *frame,
gsize flip_y)
{
GskGpuUploadTextureOp *self = (GskGpuUploadTextureOp *) op;
return gsk_gpu_upload_op_gl_command (op,
frame,
self->image,
gsk_gpu_upload_texture_op_draw);
}
static const GskGpuOpClass GSK_GPU_UPLOAD_TEXTURE_OP_CLASS = {
GSK_GPU_OP_SIZE (GskGpuUploadTextureOp),
GSK_GPU_STAGE_UPLOAD,
gsk_gpu_upload_texture_op_finish,
gsk_gpu_upload_texture_op_print,
#ifdef GDK_RENDERING_VULKAN
gsk_gpu_upload_texture_op_vk_command,
#endif
gsk_gpu_upload_texture_op_gl_command
};
GskGpuImage *
gsk_gpu_upload_texture_op (GskGpuFrame *frame,
GdkTexture *texture)
{
GskGpuUploadTextureOp *self;
self = (GskGpuUploadTextureOp *) gsk_gpu_op_alloc (frame, &GSK_GPU_UPLOAD_TEXTURE_OP_CLASS);
self->texture = g_object_ref (texture);
self->image = gsk_gpu_device_create_upload_image (gsk_gpu_frame_get_device (frame),
gdk_texture_get_format (texture),
gdk_texture_get_width (texture),
gdk_texture_get_height (texture));
return self->image;
}
typedef struct _GskGpuUploadCairoOp GskGpuUploadCairoOp;
struct _GskGpuUploadCairoOp