mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2025-01-15 23:00:08 +00:00
57c1c95e75
Frames now carry a timestamp for when they are used. This is mainly intended to attach timestamps to cached items (textures or glyphs), but it could in theory also be used when profiling. We use wallclock time here, not server time, because it's cheaper and because we're more intereseted in the local machine we're rendering on.
295 lines
8.4 KiB
C
295 lines
8.4 KiB
C
#include "config.h"
|
|
|
|
#include "gskgpurendererprivate.h"
|
|
|
|
#include "gskdebugprivate.h"
|
|
#include "gskgpudeviceprivate.h"
|
|
#include "gskgpuframeprivate.h"
|
|
#include "gskprivate.h"
|
|
#include "gskrendererprivate.h"
|
|
#include "gskrendernodeprivate.h"
|
|
#include "gskgpuimageprivate.h"
|
|
|
|
#include "gdk/gdkdisplayprivate.h"
|
|
#include "gdk/gdkdrawcontextprivate.h"
|
|
#include "gdk/gdkprofilerprivate.h"
|
|
#include "gdk/gdktextureprivate.h"
|
|
#include "gdk/gdkdrawcontextprivate.h"
|
|
|
|
#include <graphene.h>
|
|
|
|
#define GSK_GPU_MAX_FRAMES 4
|
|
|
|
typedef struct _GskGpuRendererPrivate GskGpuRendererPrivate;
|
|
|
|
struct _GskGpuRendererPrivate
|
|
{
|
|
GskGpuDevice *device;
|
|
GdkDrawContext *context;
|
|
|
|
GskGpuFrame *frames[GSK_GPU_MAX_FRAMES];
|
|
};
|
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE (GskGpuRenderer, gsk_gpu_renderer, GSK_TYPE_RENDERER)
|
|
|
|
static void
|
|
gsk_gpu_renderer_make_current (GskGpuRenderer *self)
|
|
{
|
|
GSK_GPU_RENDERER_GET_CLASS (self)->make_current (self);
|
|
}
|
|
|
|
static cairo_region_t *
|
|
get_render_region (GskGpuRenderer *self)
|
|
{
|
|
GskGpuRendererPrivate *priv = gsk_gpu_renderer_get_instance_private (self);
|
|
const cairo_region_t *damage;
|
|
cairo_region_t *scaled_damage;
|
|
GdkSurface *surface;
|
|
double scale;
|
|
|
|
surface = gdk_draw_context_get_surface (priv->context);
|
|
scale = gdk_surface_get_scale (surface);
|
|
|
|
damage = gdk_draw_context_get_frame_region (priv->context);
|
|
scaled_damage = cairo_region_create ();
|
|
for (int i = 0; i < cairo_region_num_rectangles (damage); i++)
|
|
{
|
|
cairo_rectangle_int_t rect;
|
|
cairo_region_get_rectangle (damage, i, &rect);
|
|
cairo_region_union_rectangle (scaled_damage, &(cairo_rectangle_int_t) {
|
|
.x = (int) floor (rect.x * scale),
|
|
.y = (int) floor (rect.y * scale),
|
|
.width = (int) ceil ((rect.x + rect.width) * scale) - floor (rect.x * scale),
|
|
.height = (int) ceil ((rect.y + rect.height) * scale) - floor (rect.y * scale),
|
|
});
|
|
}
|
|
|
|
return scaled_damage;
|
|
}
|
|
|
|
static GskGpuFrame *
|
|
gsk_gpu_renderer_create_frame (GskGpuRenderer *self)
|
|
{
|
|
GskGpuRendererPrivate *priv = gsk_gpu_renderer_get_instance_private (self);
|
|
GskGpuFrame *result;
|
|
|
|
result = g_object_new (GSK_GPU_RENDERER_GET_CLASS (self)->frame_type, NULL);
|
|
|
|
gsk_gpu_frame_setup (result, self, priv->device);
|
|
|
|
return result;
|
|
}
|
|
|
|
static GskGpuFrame *
|
|
gsk_gpu_renderer_get_frame (GskGpuRenderer *self)
|
|
{
|
|
GskGpuRendererPrivate *priv = gsk_gpu_renderer_get_instance_private (self);
|
|
guint i;
|
|
|
|
while (TRUE)
|
|
{
|
|
for (i = 0; i < G_N_ELEMENTS (priv->frames); i++)
|
|
{
|
|
if (priv->frames[i] == NULL)
|
|
{
|
|
priv->frames[i] = gsk_gpu_renderer_create_frame (self);
|
|
return priv->frames[i];
|
|
}
|
|
|
|
if (!gsk_gpu_frame_is_busy (priv->frames[i]))
|
|
return priv->frames[i];
|
|
}
|
|
|
|
GSK_GPU_RENDERER_GET_CLASS (self)->wait (self, priv->frames, GSK_GPU_MAX_FRAMES);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
gsk_gpu_renderer_realize (GskRenderer *renderer,
|
|
GdkSurface *surface,
|
|
GError **error)
|
|
{
|
|
GskGpuRenderer *self = GSK_GPU_RENDERER (renderer);
|
|
GskGpuRendererPrivate *priv = gsk_gpu_renderer_get_instance_private (self);
|
|
GdkDisplay *display;
|
|
|
|
if (surface)
|
|
display = gdk_surface_get_display (surface);
|
|
else
|
|
display = gdk_display_get_default ();
|
|
|
|
priv->device = GSK_GPU_RENDERER_GET_CLASS (self)->get_device (display, error);
|
|
if (priv->device == NULL)
|
|
return FALSE;
|
|
|
|
priv->context = GSK_GPU_RENDERER_GET_CLASS (self)->create_context (self, display, surface, error);
|
|
if (priv->context == NULL)
|
|
{
|
|
g_clear_object (&priv->device);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gsk_gpu_renderer_unrealize (GskRenderer *renderer)
|
|
{
|
|
GskGpuRenderer *self = GSK_GPU_RENDERER (renderer);
|
|
GskGpuRendererPrivate *priv = gsk_gpu_renderer_get_instance_private (self);
|
|
gsize i, j;
|
|
|
|
gsk_gpu_renderer_make_current (self);
|
|
|
|
while (TRUE)
|
|
{
|
|
for (i = 0, j = 0; i < G_N_ELEMENTS (priv->frames); i++)
|
|
{
|
|
if (priv->frames[i] == NULL)
|
|
break;
|
|
if (gsk_gpu_frame_is_busy (priv->frames[i]))
|
|
{
|
|
if (i > j)
|
|
{
|
|
priv->frames[j] = priv->frames[i];
|
|
priv->frames[i] = NULL;
|
|
}
|
|
j++;
|
|
continue;
|
|
}
|
|
g_clear_object (&priv->frames[i]);
|
|
}
|
|
if (j == 0)
|
|
break;
|
|
GSK_GPU_RENDERER_GET_CLASS (self)->wait (self, priv->frames, j);
|
|
}
|
|
|
|
g_clear_object (&priv->context);
|
|
g_clear_object (&priv->device);
|
|
}
|
|
|
|
static GdkTexture *
|
|
gsk_gpu_renderer_render_texture (GskRenderer *renderer,
|
|
GskRenderNode *root,
|
|
const graphene_rect_t *viewport)
|
|
{
|
|
GskGpuRenderer *self = GSK_GPU_RENDERER (renderer);
|
|
GskGpuRendererPrivate *priv = gsk_gpu_renderer_get_instance_private (self);
|
|
GskGpuFrame *frame;
|
|
GskGpuImage *image;
|
|
GdkTexture *texture;
|
|
graphene_rect_t rounded_viewport;
|
|
|
|
gsk_gpu_renderer_make_current (self);
|
|
|
|
frame = gsk_gpu_renderer_create_frame (self);
|
|
|
|
rounded_viewport = GRAPHENE_RECT_INIT (viewport->origin.x,
|
|
viewport->origin.y,
|
|
ceil (viewport->size.width),
|
|
ceil (viewport->size.height));
|
|
image = gsk_gpu_device_create_offscreen_image (priv->device,
|
|
gsk_render_node_get_preferred_depth (root),
|
|
rounded_viewport.size.width,
|
|
rounded_viewport.size.height);
|
|
|
|
texture = NULL;
|
|
gsk_gpu_frame_render (frame,
|
|
g_get_monotonic_time(),
|
|
image,
|
|
NULL,
|
|
root,
|
|
&rounded_viewport,
|
|
&texture);
|
|
|
|
g_object_unref (frame);
|
|
g_object_unref (image);
|
|
|
|
/* check that callback setting texture was actually called, as its technically async */
|
|
g_assert (texture);
|
|
|
|
return texture;
|
|
}
|
|
|
|
static void
|
|
gsk_gpu_renderer_render (GskRenderer *renderer,
|
|
GskRenderNode *root,
|
|
const cairo_region_t *region)
|
|
{
|
|
GskGpuRenderer *self = GSK_GPU_RENDERER (renderer);
|
|
GskGpuRendererPrivate *priv = gsk_gpu_renderer_get_instance_private (self);
|
|
GskGpuFrame *frame;
|
|
GskGpuImage *backbuffer;
|
|
cairo_region_t *render_region;
|
|
GdkSurface *surface;
|
|
|
|
if (cairo_region_is_empty (region))
|
|
{
|
|
gdk_draw_context_empty_frame (priv->context);
|
|
return;
|
|
}
|
|
|
|
gdk_draw_context_begin_frame_full (priv->context,
|
|
gsk_render_node_get_preferred_depth (root),
|
|
region);
|
|
|
|
gsk_gpu_renderer_make_current (self);
|
|
|
|
backbuffer = GSK_GPU_RENDERER_GET_CLASS (self)->get_backbuffer (self);
|
|
|
|
frame = gsk_gpu_renderer_get_frame (self);
|
|
render_region = get_render_region (self);
|
|
surface = gdk_draw_context_get_surface (priv->context);
|
|
|
|
gsk_gpu_frame_render (frame,
|
|
g_get_monotonic_time(),
|
|
backbuffer,
|
|
render_region,
|
|
root,
|
|
&GRAPHENE_RECT_INIT (
|
|
0, 0,
|
|
gdk_surface_get_width (surface),
|
|
gdk_surface_get_height (surface)
|
|
),
|
|
NULL);
|
|
|
|
gdk_draw_context_end_frame (priv->context);
|
|
|
|
g_clear_pointer (&render_region, cairo_region_destroy);
|
|
}
|
|
|
|
static void
|
|
gsk_gpu_renderer_class_init (GskGpuRendererClass *klass)
|
|
{
|
|
GskRendererClass *renderer_class = GSK_RENDERER_CLASS (klass);
|
|
|
|
renderer_class->realize = gsk_gpu_renderer_realize;
|
|
renderer_class->unrealize = gsk_gpu_renderer_unrealize;
|
|
renderer_class->render = gsk_gpu_renderer_render;
|
|
renderer_class->render_texture = gsk_gpu_renderer_render_texture;
|
|
|
|
gsk_ensure_resources ();
|
|
}
|
|
|
|
static void
|
|
gsk_gpu_renderer_init (GskGpuRenderer *self)
|
|
{
|
|
}
|
|
|
|
GdkDrawContext *
|
|
gsk_gpu_renderer_get_context (GskGpuRenderer *self)
|
|
{
|
|
GskGpuRendererPrivate *priv = gsk_gpu_renderer_get_instance_private (self);
|
|
|
|
return priv->context;
|
|
}
|
|
|
|
GskGpuDevice *
|
|
gsk_gpu_renderer_get_device (GskGpuRenderer *self)
|
|
{
|
|
GskGpuRendererPrivate *priv = gsk_gpu_renderer_get_instance_private (self);
|
|
|
|
return priv->device;
|
|
}
|
|
|