1/* GSK - The GTK Scene Kit
2 *
3 * Copyright 2016 Endless
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19/**
20 * GskRenderer:
21 *
22 * `GskRenderer` is a class that renders a scene graph defined via a
23 * tree of [class@Gsk.RenderNode] instances.
24 *
25 * Typically you will use a `GskRenderer` instance to repeatedly call
26 * [method@Gsk.Renderer.render] to update the contents of its associated
27 * [class@Gdk.Surface].
28 *
29 * It is necessary to realize a `GskRenderer` instance using
30 * [method@Gsk.Renderer.realize] before calling [method@Gsk.Renderer.render],
31 * in order to create the appropriate windowing system resources needed
32 * to render the scene.
33 */
34
35#include "config.h"
36
37#include "gskrendererprivate.h"
38
39#include "gskcairorenderer.h"
40#include "gskdebugprivate.h"
41#include "gl/gskglrenderer.h"
42#include "gskprofilerprivate.h"
43#include "gskrendernodeprivate.h"
44
45#include "gskenumtypes.h"
46
47#include <graphene-gobject.h>
48#include <cairo-gobject.h>
49#include <gdk/gdk.h>
50
51#ifdef GDK_WINDOWING_X11
52#include <gdk/x11/gdkx.h>
53#endif
54#ifdef GDK_WINDOWING_WAYLAND
55#include <gdk/wayland/gdkwayland.h>
56#endif
57#ifdef GDK_WINDOWING_BROADWAY
58#include "broadway/gskbroadwayrenderer.h"
59#endif
60#ifdef GDK_RENDERING_VULKAN
61#include "vulkan/gskvulkanrenderer.h"
62#endif
63#ifdef GDK_WINDOWING_MACOS
64#include <gdk/macos/gdkmacos.h>
65#endif
66#ifdef GDK_WINDOWING_WIN32
67#include <gdk/win32/gdkwin32.h>
68
69/* Remove these lines when OpenGL/ES 2.0 shader is ready */
70#include "win32/gdkprivate-win32.h"
71#include "win32/gdkdisplay-win32.h"
72#endif
73
74typedef struct
75{
76 GObject parent_instance;
77
78 GdkSurface *surface;
79 GskRenderNode *prev_node;
80 GskRenderNode *root_node;
81
82 GskProfiler *profiler;
83
84 GskDebugFlags debug_flags;
85
86 gboolean is_realized : 1;
87} GskRendererPrivate;
88
89G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GskRenderer, gsk_renderer, G_TYPE_OBJECT)
90
91enum {
92 PROP_0,
93 PROP_REALIZED,
94 PROP_SURFACE,
95
96 N_PROPS
97};
98
99static GParamSpec *gsk_renderer_properties[N_PROPS];
100
101#define GSK_RENDERER_WARN_NOT_IMPLEMENTED_METHOD(obj,method) \
102 g_critical ("Renderer of type '%s' does not implement GskRenderer::" # method, G_OBJECT_TYPE_NAME (obj))
103
104static gboolean
105gsk_renderer_real_realize (GskRenderer *self,
106 GdkSurface *surface,
107 GError **error)
108{
109 GSK_RENDERER_WARN_NOT_IMPLEMENTED_METHOD (self, realize);
110 return FALSE;
111}
112
113static void
114gsk_renderer_real_unrealize (GskRenderer *self)
115{
116 GSK_RENDERER_WARN_NOT_IMPLEMENTED_METHOD (self, unrealize);
117}
118
119static GdkTexture *
120gsk_renderer_real_render_texture (GskRenderer *self,
121 GskRenderNode *root,
122 const graphene_rect_t *viewport)
123{
124 GSK_RENDERER_WARN_NOT_IMPLEMENTED_METHOD (self, render_texture);
125 return NULL;
126}
127
128static void
129gsk_renderer_real_render (GskRenderer *self,
130 GskRenderNode *root,
131 const cairo_region_t *region)
132{
133 GSK_RENDERER_WARN_NOT_IMPLEMENTED_METHOD (self, render);
134}
135
136static void
137gsk_renderer_dispose (GObject *gobject)
138{
139 GskRenderer *self = GSK_RENDERER (gobject);
140 GskRendererPrivate *priv = gsk_renderer_get_instance_private (self);
141
142 /* We can't just unrealize here because superclasses have already run dispose.
143 * So we insist that unrealize must be called before unreffing. */
144 g_assert (!priv->is_realized);
145
146 g_clear_object (&priv->profiler);
147
148 G_OBJECT_CLASS (gsk_renderer_parent_class)->dispose (gobject);
149}
150
151static void
152gsk_renderer_get_property (GObject *gobject,
153 guint prop_id,
154 GValue *value,
155 GParamSpec *pspec)
156{
157 GskRenderer *self = GSK_RENDERER (gobject);
158 GskRendererPrivate *priv = gsk_renderer_get_instance_private (self);
159
160 switch (prop_id)
161 {
162 case PROP_REALIZED:
163 g_value_set_boolean (value, v_boolean: priv->is_realized);
164 break;
165
166 case PROP_SURFACE:
167 g_value_set_object (value, v_object: priv->surface);
168 break;
169
170 default:
171 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
172 break;
173 }
174}
175
176static void
177gsk_renderer_class_init (GskRendererClass *klass)
178{
179 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
180
181 klass->realize = gsk_renderer_real_realize;
182 klass->unrealize = gsk_renderer_real_unrealize;
183 klass->render = gsk_renderer_real_render;
184 klass->render_texture = gsk_renderer_real_render_texture;
185
186 gobject_class->get_property = gsk_renderer_get_property;
187 gobject_class->dispose = gsk_renderer_dispose;
188
189 /**
190 * GskRenderer:realized: (attributes org.gtk.Property.get=gsk_renderer_is_realized)
191 *
192 * Whether the renderer has been associated with a surface or draw context.
193 */
194 gsk_renderer_properties[PROP_REALIZED] =
195 g_param_spec_boolean (name: "realized",
196 nick: "Realized",
197 blurb: "The renderer has been associated with a surface or draw context",
198 FALSE,
199 flags: G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
200
201 /**
202 * GskRenderer:surface: (attributes org.gtk.Property.get=gsk_renderer_get_surface)
203 *
204 * The surface associated with renderer.
205 */
206 gsk_renderer_properties[PROP_SURFACE] =
207 g_param_spec_object (name: "surface",
208 nick: "Surface",
209 blurb: "The surface associated to the renderer",
210 GDK_TYPE_SURFACE,
211 flags: G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
212
213 g_object_class_install_properties (oclass: gobject_class, n_pspecs: N_PROPS, pspecs: gsk_renderer_properties);
214}
215
216static void
217gsk_renderer_init (GskRenderer *self)
218{
219 GskRendererPrivate *priv = gsk_renderer_get_instance_private (self);
220
221 priv->profiler = gsk_profiler_new ();
222 priv->debug_flags = gsk_get_debug_flags ();
223}
224
225/**
226 * gsk_renderer_get_surface: (attributes org.gtk.Method.get_property=surface)
227 * @renderer: a `GskRenderer`
228 *
229 * Retrieves the `GdkSurface` set using gsk_enderer_realize().
230 *
231 * If the renderer has not been realized yet, %NULL will be returned.
232 *
233 * Returns: (transfer none) (nullable): a `GdkSurface`
234 */
235GdkSurface *
236gsk_renderer_get_surface (GskRenderer *renderer)
237{
238 GskRendererPrivate *priv = gsk_renderer_get_instance_private (self: renderer);
239
240 g_return_val_if_fail (GSK_IS_RENDERER (renderer), NULL);
241
242 return priv->surface;
243}
244
245/*< private >
246 * gsk_renderer_get_root_node:
247 * @renderer: a `GskRenderer`
248 *
249 * Retrieves the `GskRenderNode` used by @renderer.
250 *
251 * Returns: (transfer none) (nullable): a `GskRenderNode`
252 */
253GskRenderNode *
254gsk_renderer_get_root_node (GskRenderer *renderer)
255{
256 GskRendererPrivate *priv = gsk_renderer_get_instance_private (self: renderer);
257
258 g_return_val_if_fail (GSK_IS_RENDERER (renderer), NULL);
259
260 return priv->root_node;
261}
262
263/**
264 * gsk_renderer_is_realized: (attributes org.gtk.Method.get_property=realized)
265 * @renderer: a `GskRenderer`
266 *
267 * Checks whether the @renderer is realized or not.
268 *
269 * Returns: %TRUE if the `GskRenderer` was realized, and %FALSE otherwise
270 */
271gboolean
272gsk_renderer_is_realized (GskRenderer *renderer)
273{
274 GskRendererPrivate *priv = gsk_renderer_get_instance_private (self: renderer);
275
276 g_return_val_if_fail (GSK_IS_RENDERER (renderer), FALSE);
277
278 return priv->is_realized;
279}
280
281/**
282 * gsk_renderer_realize:
283 * @renderer: a `GskRenderer`
284 * @surface: (nullable): the `GdkSurface` renderer will be used on
285 * @error: return location for an error
286 *
287 * Creates the resources needed by the @renderer to render the scene
288 * graph.
289 *
290 * Since GTK 4.6, the surface may be `NULL`, which allows using
291 * renderers without having to create a surface.
292 *
293 * Note that it is mandatory to call [method@Gsk.Renderer.unrealize] before
294 * destroying the renderer.
295 *
296 * Returns: Whether the renderer was successfully realized
297 */
298gboolean
299gsk_renderer_realize (GskRenderer *renderer,
300 GdkSurface *surface,
301 GError **error)
302{
303 GskRendererPrivate *priv = gsk_renderer_get_instance_private (self: renderer);
304
305 g_return_val_if_fail (GSK_IS_RENDERER (renderer), FALSE);
306 g_return_val_if_fail (!gsk_renderer_is_realized (renderer), FALSE);
307 g_return_val_if_fail (surface == NULL || GDK_IS_SURFACE (surface), FALSE);
308 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
309
310 if (surface)
311 priv->surface = g_object_ref (surface);
312
313 if (!GSK_RENDERER_GET_CLASS (renderer)->realize (renderer, surface, error))
314 {
315 g_clear_object (&priv->surface);
316 return FALSE;
317 }
318
319 priv->is_realized = TRUE;
320 return TRUE;
321}
322
323/**
324 * gsk_renderer_unrealize:
325 * @renderer: a `GskRenderer`
326 *
327 * Releases all the resources created by gsk_renderer_realize().
328 */
329void
330gsk_renderer_unrealize (GskRenderer *renderer)
331{
332 GskRendererPrivate *priv = gsk_renderer_get_instance_private (self: renderer);
333
334 g_return_if_fail (GSK_IS_RENDERER (renderer));
335
336 if (!priv->is_realized)
337 return;
338
339 GSK_RENDERER_GET_CLASS (renderer)->unrealize (renderer);
340
341 g_clear_pointer (&priv->prev_node, gsk_render_node_unref);
342
343 priv->is_realized = FALSE;
344}
345
346/**
347 * gsk_renderer_render_texture:
348 * @renderer: a realized `GskRenderer`
349 * @root: a `GskRenderNode`
350 * @viewport: (nullable): the section to draw or %NULL to use @root's bounds
351 *
352 * Renders the scene graph, described by a tree of `GskRenderNode` instances,
353 * to a `GdkTexture`.
354 *
355 * The @renderer will acquire a reference on the `GskRenderNode` tree while
356 * the rendering is in progress.
357 *
358 * If you want to apply any transformations to @root, you should put it into a
359 * transform node and pass that node instead.
360 *
361 * Returns: (transfer full): a `GdkTexture` with the rendered contents of @root.
362 */
363GdkTexture *
364gsk_renderer_render_texture (GskRenderer *renderer,
365 GskRenderNode *root,
366 const graphene_rect_t *viewport)
367{
368 GskRendererPrivate *priv = gsk_renderer_get_instance_private (self: renderer);
369 graphene_rect_t real_viewport;
370 GdkTexture *texture;
371
372 g_return_val_if_fail (GSK_IS_RENDERER (renderer), NULL);
373 g_return_val_if_fail (priv->is_realized, NULL);
374 g_return_val_if_fail (GSK_IS_RENDER_NODE (root), NULL);
375 g_return_val_if_fail (priv->root_node == NULL, NULL);
376
377 priv->root_node = gsk_render_node_ref (node: root);
378
379 if (viewport == NULL)
380 {
381 gsk_render_node_get_bounds (node: root, bounds: &real_viewport);
382 viewport = &real_viewport;
383 }
384
385 texture = GSK_RENDERER_GET_CLASS (renderer)->render_texture (renderer, root, viewport);
386
387#ifdef G_ENABLE_DEBUG
388 if (GSK_RENDERER_DEBUG_CHECK (renderer, RENDERER))
389 {
390 GString *buf = g_string_new (init: "*** Texture stats ***\n\n");
391
392 gsk_profiler_append_counters (profiler: priv->profiler, buffer: buf);
393 g_string_append_c (buf, '\n');
394
395 gsk_profiler_append_timers (profiler: priv->profiler, buffer: buf);
396 g_string_append_c (buf, '\n');
397
398 g_print (format: "%s\n***\n\n", buf->str);
399
400 g_string_free (string: buf, TRUE);
401 }
402#endif
403
404 g_clear_pointer (&priv->root_node, gsk_render_node_unref);
405
406 return texture;
407}
408
409/**
410 * gsk_renderer_render:
411 * @renderer: a realized `GskRenderer`
412 * @root: a `GskRenderNode`
413 * @region: (nullable): the `cairo_region_t` that must be redrawn or %NULL
414 * for the whole window
415 *
416 * Renders the scene graph, described by a tree of `GskRenderNode` instances
417 * to the renderer's surface, ensuring that the given @region gets redrawn.
418 *
419 * If the renderer has no associated surface, this function does nothing.
420 *
421 * Renderers must ensure that changes of the contents given by the @root
422 * node as well as the area given by @region are redrawn. They are however
423 * free to not redraw any pixel outside of @region if they can guarantee that
424 * it didn't change.
425 *
426 * The @renderer will acquire a reference on the `GskRenderNode` tree while
427 * the rendering is in progress.
428 */
429void
430gsk_renderer_render (GskRenderer *renderer,
431 GskRenderNode *root,
432 const cairo_region_t *region)
433{
434 GskRendererPrivate *priv = gsk_renderer_get_instance_private (self: renderer);
435 cairo_region_t *clip;
436
437 g_return_if_fail (GSK_IS_RENDERER (renderer));
438 g_return_if_fail (priv->is_realized);
439 g_return_if_fail (GSK_IS_RENDER_NODE (root));
440 g_return_if_fail (priv->root_node == NULL);
441
442 if (priv->surface == NULL)
443 return;
444
445 if (region == NULL || priv->prev_node == NULL || GSK_RENDERER_DEBUG_CHECK (renderer, FULL_REDRAW))
446 {
447 clip = cairo_region_create_rectangle (rectangle: &(GdkRectangle) {
448 0, 0,
449 gdk_surface_get_width (surface: priv->surface),
450 gdk_surface_get_height (surface: priv->surface)
451 });
452 }
453 else
454 {
455 clip = cairo_region_copy (original: region);
456 gsk_render_node_diff (node1: priv->prev_node, node2: root, region: clip);
457
458 if (cairo_region_is_empty (region: clip))
459 {
460 cairo_region_destroy (region: clip);
461 return;
462 }
463 }
464
465 priv->root_node = gsk_render_node_ref (node: root);
466
467 GSK_RENDERER_GET_CLASS (renderer)->render (renderer, root, clip);
468
469#ifdef G_ENABLE_DEBUG
470 if (GSK_RENDERER_DEBUG_CHECK (renderer, RENDERER))
471 {
472 GString *buf = g_string_new (init: "*** Frame stats ***\n\n");
473
474 gsk_profiler_append_counters (profiler: priv->profiler, buffer: buf);
475 g_string_append_c (buf, '\n');
476
477 gsk_profiler_append_timers (profiler: priv->profiler, buffer: buf);
478 g_string_append_c (buf, '\n');
479
480 g_print (format: "%s\n***\n\n", buf->str);
481
482 g_string_free (string: buf, TRUE);
483 }
484#endif
485
486 g_clear_pointer (&priv->prev_node, gsk_render_node_unref);
487 cairo_region_destroy (region: clip);
488 priv->prev_node = priv->root_node;
489 priv->root_node = NULL;
490}
491
492/*< private >
493 * gsk_renderer_get_profiler:
494 * @renderer: a `GskRenderer`
495 *
496 * Retrieves a pointer to the `GskProfiler` instance of the renderer.
497 *
498 * Returns: (transfer none): the profiler
499 */
500GskProfiler *
501gsk_renderer_get_profiler (GskRenderer *renderer)
502{
503 GskRendererPrivate *priv = gsk_renderer_get_instance_private (self: renderer);
504
505 g_return_val_if_fail (GSK_IS_RENDERER (renderer), NULL);
506
507 return priv->profiler;
508}
509
510static GType
511get_renderer_for_name (const char *renderer_name)
512{
513 if (renderer_name == NULL)
514 return G_TYPE_INVALID;
515#ifdef GDK_WINDOWING_BROADWAY
516 else if (g_ascii_strcasecmp (renderer_name, "broadway") == 0)
517 return GSK_TYPE_BROADWAY_RENDERER;
518#endif
519 else if (g_ascii_strcasecmp (s1: renderer_name, s2: "cairo") == 0)
520 return GSK_TYPE_CAIRO_RENDERER;
521 else if (g_ascii_strcasecmp (s1: renderer_name, s2: "opengl") == 0 ||
522 g_ascii_strcasecmp (s1: renderer_name, s2: "gl") == 0)
523 return GSK_TYPE_GL_RENDERER;
524#ifdef GDK_RENDERING_VULKAN
525 else if (g_ascii_strcasecmp (renderer_name, "vulkan") == 0)
526 return GSK_TYPE_VULKAN_RENDERER;
527#endif
528 else if (g_ascii_strcasecmp (s1: renderer_name, s2: "help") == 0)
529 {
530 g_print (format: "Supported arguments for GSK_RENDERER environment variable:\n");
531#ifdef GDK_WINDOWING_BROADWAY
532 g_print ("broadway - Use the Broadway specific renderer\n");
533#else
534 g_print (format: "broadway - Disabled during GTK build\n");
535#endif
536 g_print (format: " cairo - Use the Cairo fallback renderer\n");
537 g_print (format: " opengl - Use the OpenGL renderer\n");
538 g_print (format: " gl - Use the OpenGL renderer\n");
539#ifdef GDK_RENDERING_VULKAN
540 g_print (" vulkan - Use the Vulkan renderer\n");
541#else
542 g_print (format: " vulkan - Disabled during GTK build\n");
543#endif
544 g_print (format: " help - Print this help\n\n");
545 g_print (format: "Other arguments will cause a warning and be ignored.\n");
546 }
547 else
548 {
549 g_warning ("Unrecognized renderer \"%s\". Try GSK_RENDERER=help", renderer_name);
550 }
551
552 return G_TYPE_INVALID;
553}
554
555static GType
556get_renderer_for_display (GdkSurface *surface)
557{
558 GdkDisplay *display = gdk_surface_get_display (surface);
559 const char *renderer_name;
560
561 renderer_name = g_object_get_data (G_OBJECT (display), key: "gsk-renderer");
562 return get_renderer_for_name (renderer_name);
563}
564
565static GType
566get_renderer_for_env_var (GdkSurface *surface)
567{
568 static GType env_var_type = G_TYPE_NONE;
569
570 if (env_var_type == G_TYPE_NONE)
571 {
572 const char *renderer_name = g_getenv (variable: "GSK_RENDERER");
573 env_var_type = get_renderer_for_name (renderer_name);
574 }
575
576 return env_var_type;
577}
578
579static GType
580get_renderer_for_backend (GdkSurface *surface)
581{
582#ifdef GDK_WINDOWING_X11
583 if (GDK_IS_X11_SURFACE (surface))
584 return GSK_TYPE_GL_RENDERER;
585#endif
586#ifdef GDK_WINDOWING_WAYLAND
587 if (GDK_IS_WAYLAND_SURFACE (surface))
588 return GSK_TYPE_GL_RENDERER;
589#endif
590#ifdef GDK_WINDOWING_BROADWAY
591 if (GDK_IS_BROADWAY_SURFACE (surface))
592 return GSK_TYPE_BROADWAY_RENDERER;
593#endif
594#ifdef GDK_WINDOWING_MACOS
595 if (GDK_IS_MACOS_SURFACE (surface))
596 return GSK_TYPE_GL_RENDERER;
597#endif
598#ifdef GDK_WINDOWING_WIN32
599 if (GDK_IS_WIN32_SURFACE (surface))
600 return GSK_TYPE_GL_RENDERER;
601#endif
602
603 return G_TYPE_INVALID;
604}
605
606static GType
607get_renderer_fallback (GdkSurface *surface)
608{
609 return GSK_TYPE_CAIRO_RENDERER;
610}
611
612static struct {
613 GType (* get_renderer) (GdkSurface *surface);
614} renderer_possibilities[] = {
615 { get_renderer_for_display },
616 { get_renderer_for_env_var },
617 { get_renderer_for_backend },
618 { get_renderer_fallback },
619};
620
621/**
622 * gsk_renderer_new_for_surface:
623 * @surface: a `GdkSurface`
624 *
625 * Creates an appropriate `GskRenderer` instance for the given @surface.
626 *
627 * If the `GSK_RENDERER` environment variable is set, GSK will
628 * try that renderer first, before trying the backend-specific
629 * default. The ultimate fallback is the cairo renderer.
630 *
631 * The renderer will be realized before it is returned.
632 *
633 * Returns: (transfer full) (nullable): a `GskRenderer`
634 */
635GskRenderer *
636gsk_renderer_new_for_surface (GdkSurface *surface)
637{
638 GType renderer_type;
639 GskRenderer *renderer;
640 GError *error = NULL;
641 guint i;
642
643 g_return_val_if_fail (GDK_IS_SURFACE (surface), NULL);
644
645 for (i = 0; i < G_N_ELEMENTS (renderer_possibilities); i++)
646 {
647 renderer_type = renderer_possibilities[i].get_renderer (surface);
648 if (renderer_type == G_TYPE_INVALID)
649 continue;
650
651 renderer = g_object_new (object_type: renderer_type, NULL);
652
653 if (gsk_renderer_realize (renderer, surface, error: &error))
654 {
655 GSK_RENDERER_NOTE (renderer, RENDERER,
656 g_message ("Using renderer of type '%s' for surface '%s'\n",
657 G_OBJECT_TYPE_NAME (renderer),
658 G_OBJECT_TYPE_NAME (surface)));
659 return renderer;
660 }
661
662 g_message ("Failed to realize renderer of type '%s' for surface '%s': %s\n",
663 G_OBJECT_TYPE_NAME (renderer),
664 G_OBJECT_TYPE_NAME (surface),
665 error->message);
666 g_object_unref (object: renderer);
667 g_clear_error (err: &error);
668 }
669
670 g_assert_not_reached ();
671 return NULL;
672}
673
674GskDebugFlags
675gsk_renderer_get_debug_flags (GskRenderer *renderer)
676{
677 GskRendererPrivate *priv = gsk_renderer_get_instance_private (self: renderer);
678
679 g_return_val_if_fail (GSK_IS_RENDERER (renderer), 0);
680
681 return priv->debug_flags;
682}
683
684void
685gsk_renderer_set_debug_flags (GskRenderer *renderer,
686 GskDebugFlags flags)
687{
688 GskRendererPrivate *priv = gsk_renderer_get_instance_private (self: renderer);
689
690 g_return_if_fail (GSK_IS_RENDERER (renderer));
691
692 priv->debug_flags = flags;
693}
694

source code of gtk/gsk/gskrenderer.c