1/* gskglshadowlibrary.c
2 *
3 * Copyright 2020 Christian Hergert <chergert@redhat.com>
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.1 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 program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: LGPL-2.1-or-later
19 */
20
21#include "config.h"
22
23#include <string.h>
24
25#include "gskgldriverprivate.h"
26#include "gskglshadowlibraryprivate.h"
27
28#define MAX_UNUSED_FRAMES (16 * 5)
29
30struct _GskGLShadowLibrary
31{
32 GObject parent_instance;
33 GskGLDriver *driver;
34 GArray *shadows;
35};
36
37typedef struct _Shadow
38{
39 GskRoundedRect outline;
40 float blur_radius;
41 guint texture_id;
42 gint64 last_used_in_frame;
43} Shadow;
44
45G_DEFINE_TYPE (GskGLShadowLibrary, gsk_gl_shadow_library, G_TYPE_OBJECT)
46
47enum {
48 PROP_0,
49 PROP_DRIVER,
50 N_PROPS
51};
52
53static GParamSpec *properties [N_PROPS];
54
55GskGLShadowLibrary *
56gsk_gl_shadow_library_new (GskGLDriver *driver)
57{
58 g_return_val_if_fail (GSK_IS_GL_DRIVER (driver), NULL);
59
60 return g_object_new (GSK_TYPE_GL_SHADOW_LIBRARY,
61 first_property_name: "driver", driver,
62 NULL);
63}
64
65static void
66gsk_gl_shadow_library_dispose (GObject *object)
67{
68 GskGLShadowLibrary *self = (GskGLShadowLibrary *)object;
69
70 for (guint i = 0; i < self->shadows->len; i++)
71 {
72 const Shadow *shadow = &g_array_index (self->shadows, Shadow, i);
73 gsk_gl_driver_release_texture_by_id (self: self->driver, texture_id: shadow->texture_id);
74 }
75
76 g_clear_pointer (&self->shadows, g_array_unref);
77 g_clear_object (&self->driver);
78
79 G_OBJECT_CLASS (gsk_gl_shadow_library_parent_class)->dispose (object);
80}
81
82static void
83gsk_gl_shadow_library_get_property (GObject *object,
84 guint prop_id,
85 GValue *value,
86 GParamSpec *pspec)
87{
88 GskGLShadowLibrary *self = GSK_GL_SHADOW_LIBRARY (ptr: object);
89
90 switch (prop_id)
91 {
92 case PROP_DRIVER:
93 g_value_set_object (value, v_object: self->driver);
94 break;
95
96 default:
97 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
98 }
99}
100
101static void
102gsk_gl_shadow_library_set_property (GObject *object,
103 guint prop_id,
104 const GValue *value,
105 GParamSpec *pspec)
106{
107 GskGLShadowLibrary *self = GSK_GL_SHADOW_LIBRARY (ptr: object);
108
109 switch (prop_id)
110 {
111 case PROP_DRIVER:
112 self->driver = g_value_dup_object (value);
113 break;
114
115 default:
116 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
117 }
118}
119
120static void
121gsk_gl_shadow_library_class_init (GskGLShadowLibraryClass *klass)
122{
123 GObjectClass *object_class = G_OBJECT_CLASS (klass);
124
125 object_class->dispose = gsk_gl_shadow_library_dispose;
126 object_class->get_property = gsk_gl_shadow_library_get_property;
127 object_class->set_property = gsk_gl_shadow_library_set_property;
128
129 properties [PROP_DRIVER] =
130 g_param_spec_object (name: "driver",
131 nick: "Driver",
132 blurb: "Driver",
133 GSK_TYPE_GL_DRIVER,
134 flags: (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
135
136 g_object_class_install_properties (oclass: object_class, n_pspecs: N_PROPS, pspecs: properties);
137}
138
139static void
140gsk_gl_shadow_library_init (GskGLShadowLibrary *self)
141{
142 self->shadows = g_array_new (FALSE, FALSE, element_size: sizeof (Shadow));
143}
144
145void
146gsk_gl_shadow_library_insert (GskGLShadowLibrary *self,
147 const GskRoundedRect *outline,
148 float blur_radius,
149 guint texture_id)
150{
151 Shadow *shadow;
152
153 g_assert (GSK_IS_GL_SHADOW_LIBRARY (self));
154 g_assert (outline != NULL);
155 g_assert (texture_id != 0);
156
157 gsk_gl_driver_mark_texture_permanent (self: self->driver, texture_id);
158
159 g_array_set_size (array: self->shadows, length: self->shadows->len + 1);
160
161 shadow = &g_array_index (self->shadows, Shadow, self->shadows->len - 1);
162 shadow->outline = *outline;
163 shadow->blur_radius = blur_radius;
164 shadow->texture_id = texture_id;
165 shadow->last_used_in_frame = self->driver->current_frame_id;
166}
167
168guint
169gsk_gl_shadow_library_lookup (GskGLShadowLibrary *self,
170 const GskRoundedRect *outline,
171 float blur_radius)
172{
173 Shadow *ret = NULL;
174
175 g_assert (GSK_IS_GL_SHADOW_LIBRARY (self));
176 g_assert (outline != NULL);
177
178 /* Ensure GskRoundedRect is 12 packed floats without padding
179 * so that we can use memcmp instead of float comparisons.
180 */
181 G_STATIC_ASSERT (sizeof *outline == (sizeof (float) * 12));
182
183 for (guint i = 0; i < self->shadows->len; i++)
184 {
185 Shadow *shadow = &g_array_index (self->shadows, Shadow, i);
186
187 if (blur_radius == shadow->blur_radius &&
188 memcmp (s1: outline, s2: &shadow->outline, n: sizeof *outline) == 0)
189 {
190 ret = shadow;
191 break;
192 }
193 }
194
195 if (ret == NULL)
196 return 0;
197
198 g_assert (ret->texture_id != 0);
199
200 ret->last_used_in_frame = self->driver->current_frame_id;
201
202 return ret->texture_id;
203}
204
205#if 0
206static void
207write_shadow_to_png (GskGLDriver *driver,
208 const Shadow *shadow)
209{
210 int width = shadow->outline.bounds.size.width + (shadow->outline.bounds.origin.x * 2);
211 int height = shadow->outline.bounds.size.height + (shadow->outline.bounds.origin.y * 2);
212 char *filename = g_strdup_printf ("shadow_cache_%d_%d_%d.png",
213 width, height, shadow->texture_id);
214 GdkTexture *texture;
215
216 texture = gdk_gl_texture_new (gsk_gl_driver_get_context (driver),
217 shadow->texture_id,
218 width, height,
219 NULL, NULL);
220 gdk_texture_save_to_png (texture, filename);
221
222 g_object_unref (texture);
223 g_free (filename);
224}
225#endif
226
227void
228gsk_gl_shadow_library_begin_frame (GskGLShadowLibrary *self)
229{
230 gint64 watermark;
231 int i;
232 int p;
233
234 g_return_if_fail (GSK_IS_GL_SHADOW_LIBRARY (self));
235
236#if 0
237 for (i = 0, p = self->shadows->len; i < p; i++)
238 {
239 const Shadow *shadow = &g_array_index (self->shadows, Shadow, i);
240 write_shadow_to_png (self->driver, shadow);
241 }
242#endif
243
244 watermark = self->driver->current_frame_id - MAX_UNUSED_FRAMES;
245
246 for (i = 0, p = self->shadows->len; i < p; i++)
247 {
248 const Shadow *shadow = &g_array_index (self->shadows, Shadow, i);
249
250 if (shadow->last_used_in_frame < watermark)
251 {
252 gsk_gl_driver_release_texture_by_id (self: self->driver, texture_id: shadow->texture_id);
253 g_array_remove_index_fast (array: self->shadows, index_: i);
254 p--;
255 i--;
256 }
257 }
258}
259

source code of gtk/gsk/gl/gskglshadowlibrary.c