mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-12-28 22:41:43 +00:00
a0bbd14325
Pass a scale factor when caching glyphs or looking them up in the cache. The glyphs in the cache are rendered with subpixel precision determined by the scale. Update all callers to pass a scale factor according to the window scale. This lets us render crisp glyphs on hidpi systems.
375 lines
11 KiB
C
375 lines
11 KiB
C
#include "config.h"
|
|
|
|
#include "gskvulkanrendererprivate.h"
|
|
|
|
#include "gskdebugprivate.h"
|
|
#include "gskprivate.h"
|
|
#include "gskrendererprivate.h"
|
|
#include "gskrendernodeprivate.h"
|
|
#include "gsktextureprivate.h"
|
|
#include "gskvulkanbufferprivate.h"
|
|
#include "gskvulkanimageprivate.h"
|
|
#include "gskvulkanpipelineprivate.h"
|
|
#include "gskvulkanrenderprivate.h"
|
|
#include "gskvulkanglyphcacheprivate.h"
|
|
|
|
#include <graphene.h>
|
|
|
|
typedef struct _GskVulkanTextureData GskVulkanTextureData;
|
|
|
|
struct _GskVulkanTextureData {
|
|
GskTexture *texture;
|
|
GskVulkanImage *image;
|
|
GskVulkanRenderer *renderer;
|
|
};
|
|
|
|
#ifdef G_ENABLE_DEBUG
|
|
typedef struct {
|
|
GQuark frames;
|
|
GQuark render_passes;
|
|
GQuark fallback_pixels;
|
|
GQuark texture_pixels;
|
|
} ProfileCounters;
|
|
|
|
typedef struct {
|
|
GQuark cpu_time;
|
|
GQuark gpu_time;
|
|
} ProfileTimers;
|
|
#endif
|
|
|
|
struct _GskVulkanRenderer
|
|
{
|
|
GskRenderer parent_instance;
|
|
|
|
GdkVulkanContext *vulkan;
|
|
|
|
guint n_targets;
|
|
GskVulkanImage **targets;
|
|
|
|
GskVulkanRender *render;
|
|
|
|
GSList *textures;
|
|
|
|
GskVulkanGlyphCache *glyph_cache;
|
|
|
|
#ifdef G_ENABLE_DEBUG
|
|
ProfileCounters profile_counters;
|
|
ProfileTimers profile_timers;
|
|
#endif
|
|
};
|
|
|
|
struct _GskVulkanRendererClass
|
|
{
|
|
GskRendererClass parent_class;
|
|
};
|
|
|
|
G_DEFINE_TYPE (GskVulkanRenderer, gsk_vulkan_renderer, GSK_TYPE_RENDERER)
|
|
|
|
static void
|
|
gsk_vulkan_renderer_free_targets (GskVulkanRenderer *self)
|
|
{
|
|
guint i;
|
|
|
|
for (i = 0; i < self->n_targets; i++)
|
|
{
|
|
g_object_unref (self->targets[i]);
|
|
}
|
|
|
|
g_clear_pointer (&self->targets, g_free);
|
|
self->n_targets = 0;
|
|
}
|
|
|
|
static void
|
|
gsk_vulkan_renderer_update_images_cb (GdkVulkanContext *context,
|
|
GskVulkanRenderer *self)
|
|
{
|
|
GdkWindow *window;
|
|
gint scale_factor;
|
|
gsize width, height;
|
|
guint i;
|
|
|
|
gsk_vulkan_renderer_free_targets (self);
|
|
|
|
self->n_targets = gdk_vulkan_context_get_n_images (context);
|
|
self->targets = g_new (GskVulkanImage *, self->n_targets);
|
|
|
|
window = gsk_renderer_get_window (GSK_RENDERER (self));
|
|
scale_factor = gdk_window_get_scale_factor (window);
|
|
width = gdk_window_get_width (window) * scale_factor;
|
|
height = gdk_window_get_height (window) * scale_factor;
|
|
|
|
for (i = 0; i < self->n_targets; i++)
|
|
{
|
|
self->targets[i] = gsk_vulkan_image_new_for_swapchain (self->vulkan,
|
|
gdk_vulkan_context_get_image (context, i),
|
|
gdk_vulkan_context_get_image_format (self->vulkan),
|
|
width, height);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
gsk_vulkan_renderer_realize (GskRenderer *renderer,
|
|
GdkWindow *window,
|
|
GError **error)
|
|
{
|
|
GskVulkanRenderer *self = GSK_VULKAN_RENDERER (renderer);
|
|
|
|
self->vulkan = gdk_window_create_vulkan_context (window, error);
|
|
if (self->vulkan == NULL)
|
|
return FALSE;
|
|
|
|
g_signal_connect (self->vulkan,
|
|
"images-updated",
|
|
G_CALLBACK (gsk_vulkan_renderer_update_images_cb),
|
|
self);
|
|
gsk_vulkan_renderer_update_images_cb (self->vulkan, self);
|
|
|
|
self->render = gsk_vulkan_render_new (renderer, self->vulkan);
|
|
|
|
self->glyph_cache = gsk_vulkan_glyph_cache_new (self->vulkan);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gsk_vulkan_renderer_unrealize (GskRenderer *renderer)
|
|
{
|
|
GskVulkanRenderer *self = GSK_VULKAN_RENDERER (renderer);
|
|
GSList *l;
|
|
|
|
g_clear_object (&self->glyph_cache);
|
|
|
|
for (l = self->textures; l; l = l->next)
|
|
{
|
|
GskVulkanTextureData *data = l->data;
|
|
|
|
data->renderer = NULL;
|
|
gsk_texture_clear_render_data (data->texture);
|
|
}
|
|
g_clear_pointer (&self->textures, (GDestroyNotify) g_slist_free);
|
|
|
|
g_clear_pointer (&self->render, gsk_vulkan_render_free);
|
|
|
|
gsk_vulkan_renderer_free_targets (self);
|
|
g_signal_handlers_disconnect_by_func(self->vulkan,
|
|
gsk_vulkan_renderer_update_images_cb,
|
|
self);
|
|
|
|
g_clear_object (&self->vulkan);
|
|
}
|
|
|
|
static GskTexture *
|
|
gsk_vulkan_renderer_render_texture (GskRenderer *renderer,
|
|
GskRenderNode *root,
|
|
const graphene_rect_t *viewport)
|
|
{
|
|
GskVulkanRenderer *self = GSK_VULKAN_RENDERER (renderer);
|
|
GskVulkanRender *render;
|
|
GskVulkanImage *image;
|
|
GskTexture *texture;
|
|
#ifdef G_ENABLE_DEBUG
|
|
GskProfiler *profiler;
|
|
gint64 cpu_time;
|
|
#endif
|
|
|
|
#ifdef G_ENABLE_DEBUG
|
|
profiler = gsk_renderer_get_profiler (renderer);
|
|
gsk_profiler_counter_set (profiler, self->profile_counters.fallback_pixels, 0);
|
|
gsk_profiler_counter_set (profiler, self->profile_counters.texture_pixels, 0);
|
|
gsk_profiler_counter_set (profiler, self->profile_counters.render_passes, 0);
|
|
gsk_profiler_timer_begin (profiler, self->profile_timers.cpu_time);
|
|
#endif
|
|
|
|
render = gsk_vulkan_render_new (renderer, self->vulkan);
|
|
|
|
image = gsk_vulkan_image_new_for_framebuffer (self->vulkan,
|
|
ceil (viewport->size.width),
|
|
ceil (viewport->size.height));
|
|
|
|
gsk_vulkan_render_reset (render, image, viewport);
|
|
|
|
gsk_vulkan_render_add_node (render, root);
|
|
|
|
gsk_vulkan_render_upload (render);
|
|
|
|
gsk_vulkan_render_draw (render);
|
|
|
|
texture = gsk_vulkan_render_download_target (render);
|
|
|
|
g_object_unref (image);
|
|
gsk_vulkan_render_free (render);
|
|
|
|
#ifdef G_ENABLE_DEBUG
|
|
cpu_time = gsk_profiler_timer_end (profiler, self->profile_timers.cpu_time);
|
|
gsk_profiler_timer_set (profiler, self->profile_timers.cpu_time, cpu_time);
|
|
|
|
gsk_profiler_push_samples (profiler);
|
|
#endif
|
|
|
|
return texture;
|
|
}
|
|
|
|
static void
|
|
gsk_vulkan_renderer_render (GskRenderer *renderer,
|
|
GskRenderNode *root)
|
|
{
|
|
GskVulkanRenderer *self = GSK_VULKAN_RENDERER (renderer);
|
|
GskVulkanRender *render;
|
|
#ifdef G_ENABLE_DEBUG
|
|
GskProfiler *profiler;
|
|
gint64 cpu_time;
|
|
#endif
|
|
|
|
#ifdef G_ENABLE_DEBUG
|
|
profiler = gsk_renderer_get_profiler (renderer);
|
|
gsk_profiler_counter_set (profiler, self->profile_counters.fallback_pixels, 0);
|
|
gsk_profiler_counter_set (profiler, self->profile_counters.texture_pixels, 0);
|
|
gsk_profiler_counter_set (profiler, self->profile_counters.render_passes, 0);
|
|
gsk_profiler_timer_begin (profiler, self->profile_timers.cpu_time);
|
|
#endif
|
|
|
|
render = self->render;
|
|
|
|
gsk_vulkan_render_reset (render, self->targets[gdk_vulkan_context_get_draw_index (self->vulkan)], NULL);
|
|
|
|
gsk_vulkan_render_add_node (render, root);
|
|
|
|
gsk_vulkan_render_upload (render);
|
|
|
|
gsk_vulkan_render_draw (render);
|
|
|
|
#ifdef G_ENABLE_DEBUG
|
|
gsk_profiler_counter_inc (profiler, self->profile_counters.frames);
|
|
|
|
cpu_time = gsk_profiler_timer_end (profiler, self->profile_timers.cpu_time);
|
|
gsk_profiler_timer_set (profiler, self->profile_timers.cpu_time, cpu_time);
|
|
|
|
gsk_profiler_push_samples (profiler);
|
|
#endif
|
|
}
|
|
|
|
static GdkDrawingContext *
|
|
gsk_vulkan_renderer_begin_draw_frame (GskRenderer *renderer,
|
|
const cairo_region_t *region)
|
|
{
|
|
GskVulkanRenderer *self = GSK_VULKAN_RENDERER (renderer);
|
|
GdkDrawingContext *result;
|
|
|
|
result = gdk_window_begin_draw_frame (gsk_renderer_get_window (renderer),
|
|
GDK_DRAW_CONTEXT (self->vulkan),
|
|
region);
|
|
|
|
return result;
|
|
}
|
|
|
|
static void
|
|
gsk_vulkan_renderer_class_init (GskVulkanRendererClass *klass)
|
|
{
|
|
GskRendererClass *renderer_class = GSK_RENDERER_CLASS (klass);
|
|
|
|
renderer_class->realize = gsk_vulkan_renderer_realize;
|
|
renderer_class->unrealize = gsk_vulkan_renderer_unrealize;
|
|
renderer_class->render = gsk_vulkan_renderer_render;
|
|
renderer_class->render_texture = gsk_vulkan_renderer_render_texture;
|
|
renderer_class->begin_draw_frame = gsk_vulkan_renderer_begin_draw_frame;
|
|
}
|
|
|
|
static void
|
|
gsk_vulkan_renderer_init (GskVulkanRenderer *self)
|
|
{
|
|
#ifdef G_ENABLE_DEBUG
|
|
GskProfiler *profiler = gsk_renderer_get_profiler (GSK_RENDERER (self));
|
|
#endif
|
|
|
|
gsk_ensure_resources ();
|
|
|
|
#ifdef G_ENABLE_DEBUG
|
|
self->profile_counters.frames = gsk_profiler_add_counter (profiler, "frames", "Frames", FALSE);
|
|
self->profile_counters.render_passes = gsk_profiler_add_counter (profiler, "render-passes", "Render passes", FALSE);
|
|
self->profile_counters.fallback_pixels = gsk_profiler_add_counter (profiler, "fallback-pixels", "Fallback pixels", TRUE);
|
|
self->profile_counters.texture_pixels = gsk_profiler_add_counter (profiler, "texture-pixels", "Texture pixels", TRUE);
|
|
|
|
self->profile_timers.cpu_time = gsk_profiler_add_timer (profiler, "cpu-time", "CPU time", FALSE, TRUE);
|
|
if (GSK_RENDER_MODE_CHECK (SYNC))
|
|
self->profile_timers.gpu_time = gsk_profiler_add_timer (profiler, "gpu-time", "GPU time", FALSE, TRUE);
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
gsk_vulkan_renderer_clear_texture (gpointer p)
|
|
{
|
|
GskVulkanTextureData *data = p;
|
|
|
|
if (data->renderer != NULL)
|
|
data->renderer->textures = g_slist_remove (data->renderer->textures, data);
|
|
|
|
g_object_unref (data->image);
|
|
|
|
g_slice_free (GskVulkanTextureData, data);
|
|
}
|
|
|
|
GskVulkanImage *
|
|
gsk_vulkan_renderer_ref_texture_image (GskVulkanRenderer *self,
|
|
GskTexture *texture,
|
|
GskVulkanUploader *uploader)
|
|
{
|
|
GskVulkanTextureData *data;
|
|
cairo_surface_t *surface;
|
|
GskVulkanImage *image;
|
|
|
|
data = gsk_texture_get_render_data (texture, self);
|
|
if (data)
|
|
return g_object_ref (data->image);
|
|
|
|
surface = gsk_texture_download_surface (texture);
|
|
image = gsk_vulkan_image_new_from_data (uploader,
|
|
cairo_image_surface_get_data (surface),
|
|
cairo_image_surface_get_width (surface),
|
|
cairo_image_surface_get_height (surface),
|
|
cairo_image_surface_get_stride (surface));
|
|
cairo_surface_destroy (surface);
|
|
|
|
data = g_slice_new0 (GskVulkanTextureData);
|
|
data->image = image;
|
|
data->texture = texture;
|
|
data->renderer = self;
|
|
|
|
if (gsk_texture_set_render_data (texture, self, data, gsk_vulkan_renderer_clear_texture))
|
|
{
|
|
g_object_ref (data->image);
|
|
self->textures = g_slist_prepend (self->textures, data);
|
|
}
|
|
else
|
|
{
|
|
g_slice_free (GskVulkanTextureData, data);
|
|
}
|
|
|
|
return image;
|
|
}
|
|
|
|
guint
|
|
gsk_vulkan_renderer_cache_glyph (GskVulkanRenderer *self,
|
|
PangoFont *font,
|
|
PangoGlyph glyph,
|
|
float scale)
|
|
{
|
|
return gsk_vulkan_glyph_cache_lookup (self->glyph_cache, TRUE, font, glyph, scale)->texture_index;
|
|
}
|
|
|
|
GskVulkanImage *
|
|
gsk_vulkan_renderer_ref_glyph_image (GskVulkanRenderer *self,
|
|
GskVulkanUploader *uploader,
|
|
guint index)
|
|
{
|
|
return g_object_ref (gsk_vulkan_glyph_cache_get_glyph_image (self->glyph_cache, uploader, index));
|
|
}
|
|
|
|
GskVulkanCachedGlyph *
|
|
gsk_vulkan_renderer_get_cached_glyph (GskVulkanRenderer *self,
|
|
PangoFont *font,
|
|
PangoGlyph glyph,
|
|
float scale)
|
|
{
|
|
return gsk_vulkan_glyph_cache_lookup (self->glyph_cache, FALSE, font, glyph, scale);
|
|
}
|