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 | |
30 | struct _GskGLShadowLibrary |
31 | { |
32 | GObject parent_instance; |
33 | GskGLDriver *driver; |
34 | GArray *shadows; |
35 | }; |
36 | |
37 | typedef struct _Shadow |
38 | { |
39 | GskRoundedRect outline; |
40 | float blur_radius; |
41 | guint texture_id; |
42 | gint64 last_used_in_frame; |
43 | } Shadow; |
44 | |
45 | G_DEFINE_TYPE (GskGLShadowLibrary, gsk_gl_shadow_library, G_TYPE_OBJECT) |
46 | |
47 | enum { |
48 | PROP_0, |
49 | PROP_DRIVER, |
50 | N_PROPS |
51 | }; |
52 | |
53 | static GParamSpec *properties [N_PROPS]; |
54 | |
55 | GskGLShadowLibrary * |
56 | gsk_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 | |
65 | static void |
66 | gsk_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 | |
82 | static void |
83 | gsk_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 | |
101 | static void |
102 | gsk_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 | |
120 | static void |
121 | gsk_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 | |
139 | static void |
140 | gsk_gl_shadow_library_init (GskGLShadowLibrary *self) |
141 | { |
142 | self->shadows = g_array_new (FALSE, FALSE, element_size: sizeof (Shadow)); |
143 | } |
144 | |
145 | void |
146 | gsk_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 | |
168 | guint |
169 | gsk_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 |
206 | static void |
207 | write_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 | |
227 | void |
228 | gsk_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 | |