#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/gdkdebugprivate.h"
#include "gdk/gdkdisplayprivate.h"
#include "gdk/gdkdmabuftextureprivate.h"
#include "gdk/gdkmemorytextureprivate.h"
#include "gdk/gdkdrawcontextprivate.h"
#include "gdk/gdkprofilerprivate.h"
#include "gdk/gdktextureprivate.h"
#include "gdk/gdkdrawcontextprivate.h"
#include "gdk/gdkcolorstateprivate.h"
#include <graphene.h>
#define GSK_GPU_MAX_FRAMES 4
static const GdkDebugKey gsk_gpu_optimization_keys[] = {
{ "clear" , GSK_GPU_OPTIMIZE_CLEAR, "Use shaders instead of vkCmdClearAttachment()/glClear()" },
{ "merge" , GSK_GPU_OPTIMIZE_MERGE, "Use one vkCmdDraw()/glDrawArrays() per operation" },
{ "blit" , GSK_GPU_OPTIMIZE_BLIT, "Use shaders instead of vkCmdBlit()/glBlitFramebuffer()" },
{ "gradients" , GSK_GPU_OPTIMIZE_GRADIENTS, "Don't supersample gradients" },
{ "mipmap" , GSK_GPU_OPTIMIZE_MIPMAP, "Avoid creating mipmaps" },
{ "to-image" , GSK_GPU_OPTIMIZE_TO_IMAGE, "Don't fast-path creation of images for nodes" },
{ "occlusion" , GSK_GPU_OPTIMIZE_OCCLUSION_CULLING, "Disable occlusion culling via opaque node tracking" },
{ "repeat" , GSK_GPU_OPTIMIZE_REPEAT, "Repeat drawing operations instead of using offscreen and GL_REPEAT" },
{ "damage" , GSK_GPU_OPTIMIZE_DAMAGE, "Redraw the whole bounding box instead of doing fine grained damage tracking" },
{ "profile" , GSK_GPU_OPTIMIZE_PROFILE, "Disable profiling support" },
};
typedef struct _GskGpuRendererPrivate GskGpuRendererPrivate;
struct _GskGpuRendererPrivate
{
GskGpuDevice *device;
GdkDrawContext *context;
GskGpuOptimizations optimizations;
GskGpuFrame *frames[GSK_GPU_MAX_FRAMES];
};
static void gsk_gpu_renderer_dmabuf_downloader_init (GdkDmabufDownloaderInterface *iface);
G_DEFINE_TYPE_EXTENDED (GskGpuRenderer, gsk_gpu_renderer, GSK_TYPE_RENDERER, 0 ,
G_ADD_PRIVATE (GskGpuRenderer)
G_IMPLEMENT_INTERFACE (GDK_TYPE_DMABUF_DOWNLOADER,
gsk_gpu_renderer_dmabuf_downloader_init))
static void
gsk_gpu_renderer_make_current (GskGpuRenderer *self)
{
GSK_GPU_RENDERER_GET_CLASS (self)->make_current (self);
}
static gpointer
gsk_gpu_renderer_save_current (GskGpuRenderer *self)
{
return GSK_GPU_RENDERER_GET_CLASS (self)->save_current (self);
}
static void
gsk_gpu_renderer_restore_current (GskGpuRenderer *self,
gpointer current)
{
GSK_GPU_RENDERER_GET_CLASS (self)->restore_current (self, current);
}
static GskGpuFrame *
gsk_gpu_renderer_create_frame (GskGpuRenderer *self)
{
GskGpuRendererPrivate *priv = gsk_gpu_renderer_get_instance_private (self);
GskGpuRendererClass *klass = GSK_GPU_RENDERER_GET_CLASS (self);
GskGpuFrame *result;
if (GSK_RENDERER_DEBUG_CHECK (GSK_RENDERER (self), PROFILE) &&
priv->optimizations & GSK_GPU_OPTIMIZE_PROFILE)
result = g_object_new (klass->profile_frame_type, NULL);
else
result = g_object_new (klass->frame_type, NULL);
gsk_gpu_frame_setup (result, self, priv->device, priv->optimizations);
return result;
}
static GskGpuFrame *
gsk_gpu_renderer_get_frame (GskGpuRenderer *self)
{
GskGpuRendererPrivate *priv = gsk_gpu_renderer_get_instance_private (self);
GskGpuFrame *earliest_frame = NULL;
gint64 earliest_time = G_MAXINT64;
guint i;
for (i = 0 ; i < G_N_ELEMENTS (priv->frames); i++)
{
gint64 timestamp;
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];
timestamp = gsk_gpu_frame_get_timestamp (priv->frames[i]);
if (timestamp < earliest_time)
{
earliest_time = timestamp;
earliest_frame = priv->frames[i];
}
}
g_assert (earliest_frame);
gsk_gpu_frame_wait (earliest_frame);
return earliest_frame;
}
static void
gsk_gpu_renderer_dmabuf_downloader_close (GdkDmabufDownloader *downloader)
{
gsk_renderer_unrealize (GSK_RENDERER (downloader));
}
static gboolean
gsk_gpu_renderer_dmabuf_downloader_download (GdkDmabufDownloader *downloader,
GdkDmabufTexture *texture,
guchar *data,
const GdkMemoryLayout *layout,
GdkColorState *color_state)
{
GskGpuRenderer *self = GSK_GPU_RENDERER (downloader);
GskGpuRendererPrivate *priv = gsk_gpu_renderer_get_instance_private (self);
GskGpuFrame *frame;
gpointer previous;
gboolean retval = FALSE ;
gint64 timestamp;
previous = gsk_gpu_renderer_save_current (self);
timestamp = g_get_monotonic_time ();
gsk_gpu_device_maybe_gc (priv->device, timestamp);
gsk_gpu_renderer_make_current (self);
frame = gsk_gpu_renderer_get_frame (self);
if (gsk_gpu_frame_download_texture (frame,
timestamp,
GDK_TEXTURE (texture),
data,
layout,
color_state))
{
retval = TRUE ;
GDK_DISPLAY_DEBUG (gdk_dmabuf_texture_get_display (texture), DMABUF,
"Used %s for downloading %dx%d dmabuf (format %.4s:%#" G_GINT64_MODIFIER "x)" ,
G_OBJECT_TYPE_NAME (downloader),
gdk_texture_get_width (GDK_TEXTURE (texture)),
gdk_texture_get_height (GDK_TEXTURE (texture)),
(char *)&(gdk_dmabuf_texture_get_dmabuf (texture)->fourcc),
gdk_dmabuf_texture_get_dmabuf (texture)->modifier);
gsk_gpu_frame_wait (frame);
}
gsk_gpu_renderer_restore_current (self, previous);
gsk_gpu_device_queue_gc (priv->device);
return retval;
}
static void
gsk_gpu_renderer_dmabuf_downloader_init (GdkDmabufDownloaderInterface *iface)
{
iface->close = gsk_gpu_renderer_dmabuf_downloader_close;
iface->download = gsk_gpu_renderer_dmabuf_downloader_download;
}
static gboolean
gsk_gpu_renderer_realize (GskRenderer *renderer,
GdkDisplay *display,
GdkSurface *surface,
gboolean attach,
GError **error)
{
GskGpuRenderer *self = GSK_GPU_RENDERER (renderer);
GskGpuRendererPrivate *priv = gsk_gpu_renderer_get_instance_private (self);
GskGpuOptimizations context_optimizations;
G_GNUC_UNUSED gint64 start_time = GDK_PROFILER_CURRENT_TIME;
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, &context_optimizations, error);
if (priv->context == NULL)
{
g_clear_object (&priv->device);
return FALSE ;
}
if (attach && !gdk_draw_context_attach (priv->context, error))
{
g_clear_object (&priv->context);
g_clear_object (&priv->device);
return FALSE ;
}
priv->optimizations &= context_optimizations;
gdk_profiler_end_mark (start_time, "Realize GskGpuRenderer" , NULL);
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;
gsk_gpu_renderer_make_current (self);
for (i = 0 ; i < G_N_ELEMENTS (priv->frames); i++)
{
if (priv->frames[i] == NULL)
break ;
if (gsk_gpu_frame_is_busy (priv->frames[i]))
gsk_gpu_frame_wait (priv->frames[i]);
g_clear_object (&priv->frames[i]);
}
gdk_draw_context_detach (priv->context);
g_clear_object (&priv->context);
g_clear_object (&priv->device);
}
static GdkTexture *
gsk_gpu_renderer_fallback_render_texture (GskGpuRenderer *self,
gint64 timestamp,
GskRenderNode *root,
const graphene_rect_t *rounded_viewport)
{
GskGpuRendererPrivate *priv = gsk_gpu_renderer_get_instance_private (self);
GskGpuImage *image;
gsize width, height, max_size, image_width, image_height;
gsize x, y;
GdkMemoryDepth depth;
GdkColorState *color_state;
GBytes *bytes;
guchar *data;
GdkTexture *texture;
cairo_region_t *clip_region;
GskGpuFrame *frame;
GdkMemoryLayout layout;
max_size = gsk_gpu_device_get_max_image_size (priv->device);
depth = gsk_render_node_get_preferred_depth (root);
do
{
image = gsk_gpu_device_create_download_image (priv->device,
gsk_render_node_get_preferred_depth (root),
MIN (max_size, rounded_viewport->size.width),
MIN (max_size, rounded_viewport->size.height));
max_size /= 2 ;
}
while (image == NULL);
width = rounded_viewport->size.width;
height = rounded_viewport->size.height;
if (!gdk_memory_layout_try_init (&layout,
gsk_gpu_image_get_format (image),
width,
height,
1 ) ||
!(data = g_malloc (layout.size)))
{
g_critical ("Image size %zux%zu too large" , width, height);
return NULL;
}
image_width = gsk_gpu_image_get_width (image);
image_height = gsk_gpu_image_get_height (image);
if (gsk_gpu_image_get_conversion (image) & GSK_GPU_CONVERSION_SRGB)
color_state = GDK_COLOR_STATE_SRGB_LINEAR;
else
color_state = GDK_COLOR_STATE_SRGB;
for (y = 0 ; y < height; y += image_height)
{
for (x = 0 ; x < width; x += image_width)
{
gsize tile_width, tile_height;
GdkMemoryLayout sub;
tile_width = MIN (image_width, width - x);
tile_height = MIN (image_height, height - y);
texture = NULL;
if (image == NULL)
{
image = gsk_gpu_device_create_download_image (priv->device,
depth,
tile_width, tile_height);
if (gsk_gpu_image_get_conversion (image) & GSK_GPU_CONVERSION_SRGB)
color_state = GDK_COLOR_STATE_SRGB_LINEAR;
else
color_state = GDK_COLOR_STATE_SRGB;
}
clip_region = cairo_region_create_rectangle (&(cairo_rectangle_int_t) {
0 , 0 ,
tile_width, tile_height
});
frame = gsk_gpu_renderer_get_frame (self);
gsk_gpu_frame_render (frame,
timestamp,
image,
color_state,
clip_region,
root,
&GRAPHENE_RECT_INIT (rounded_viewport->origin.x + x,
rounded_viewport->origin.y + y,
tile_width,
tile_height),
&texture);
gsk_gpu_frame_sync (frame);
gsk_gpu_frame_wait (frame);
g_assert (texture);
gdk_memory_layout_init_sublayout (&sub,
&layout,
&(cairo_rectangle_int_t) {
x,
y,
tile_width,
tile_height
});
gdk_texture_do_download (texture, data, &sub, color_state);
g_object_unref (texture);
g_clear_object (&image);
/* Let's GC like a madman, we draw oversized stuff and don't want to OOM */
gsk_gpu_device_maybe_gc (priv->device, timestamp);
gsk_gpu_renderer_make_current (self);
}
}
bytes = g_bytes_new_take (data, layout.size);
texture = gdk_memory_texture_new_from_layout (bytes, &layout, color_state, NULL, NULL);
g_bytes_unref (bytes);
return texture;
}
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;
GdkColorState *color_state;
cairo_region_t *clip_region;
gint64 timestamp;
timestamp = g_get_monotonic_time ();
gsk_gpu_device_maybe_gc (priv->device, timestamp);
gsk_gpu_renderer_make_current (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_download_image (priv->device,
gsk_render_node_get_preferred_depth (root),
rounded_viewport.size.width,
rounded_viewport.size.height);
if (image == NULL)
return gsk_gpu_renderer_fallback_render_texture (self, timestamp, root, &rounded_viewport);
if (gsk_gpu_image_get_conversion (image) == GSK_GPU_CONVERSION_SRGB)
color_state = GDK_COLOR_STATE_SRGB_LINEAR;
else
color_state = GDK_COLOR_STATE_SRGB;
frame = gsk_gpu_renderer_get_frame (self);
clip_region = cairo_region_create_rectangle (&(cairo_rectangle_int_t) {
0 , 0 ,
gsk_gpu_image_get_width (image),
gsk_gpu_image_get_height (image)
});
texture = NULL;
gsk_gpu_frame_render (frame,
g_get_monotonic_time (),
image,
color_state,
clip_region,
root,
&rounded_viewport,
&texture);
gsk_gpu_frame_sync (frame);
gsk_gpu_frame_wait (frame);
g_object_unref (image);
gsk_gpu_device_queue_gc (priv->device);
/* 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;
graphene_rect_t opaque_tmp;
const graphene_rect_t *opaque;
double scale;
GdkMemoryDepth depth;
gint64 timestamp;
if (cairo_region_is_empty (region))
{
gdk_draw_context_empty_frame (priv->context);
return ;
}
timestamp = g_get_monotonic_time ();
gsk_gpu_device_maybe_gc (priv->device, timestamp);
gsk_gpu_renderer_make_current (self);
depth = gsk_render_node_get_preferred_depth (root);
frame = gsk_gpu_renderer_get_frame (self);
scale = gdk_surface_get_scale (gdk_draw_context_get_surface (priv->context));
if (gsk_render_node_get_opaque_rect (root, &opaque_tmp))
opaque = &opaque_tmp;
else
opaque = NULL;
gsk_gpu_frame_begin (frame, priv->context, depth, region, opaque);
backbuffer = GSK_GPU_RENDERER_GET_CLASS (self)->get_backbuffer (self);
render_region = cairo_region_copy (gdk_draw_context_get_render_region (priv->context));
if (!(priv->optimizations & GSK_GPU_OPTIMIZE_DAMAGE))
{
cairo_rectangle_int_t extents;
cairo_region_get_extents (render_region, &extents);
cairo_region_destroy (render_region);
render_region = cairo_region_create_rectangle (&extents);
}
gsk_gpu_frame_render (frame,
timestamp,
backbuffer,
gdk_draw_context_get_color_state (priv->context),
render_region,
root,
&GRAPHENE_RECT_INIT (
0 , 0 ,
gsk_gpu_image_get_width (backbuffer) / scale,
gsk_gpu_image_get_height (backbuffer) / scale
),
NULL);
gsk_gpu_frame_end (frame, priv->context);
g_object_unref (backbuffer);
gsk_gpu_device_queue_gc (priv->device);
}
static void
gsk_gpu_renderer_class_init (GskGpuRendererClass *klass)
{
GskRendererClass *renderer_class = GSK_RENDERER_CLASS (klass);
renderer_class->supports_offload = TRUE ;
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 ();
klass->optimizations = -1 ;
klass->optimizations &= ~gdk_parse_debug_var ("GSK_GPU_DISABLE" ,
"GSK_GPU_DISABLE can be set to of values which cause GSK to disable\n"
"certain optimizations in the \'ngl\' and \'vulkan\' renderers.\n" ,
gsk_gpu_optimization_keys,
G_N_ELEMENTS (gsk_gpu_optimization_keys));
}
static void
gsk_gpu_renderer_init (GskGpuRenderer *self)
{
GskGpuRendererPrivate *priv = gsk_gpu_renderer_get_instance_private (self);
priv->optimizations = GSK_GPU_RENDERER_GET_CLASS (self)->optimizations;
}
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;
}
Messung V0.5 in Prozent C=98 H=92 G=94
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.2Bemerkung:
(vorverarbeitet am 2026-07-02)
¤
*Bot Zugriff