1 | /* gskgluniformstate.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 <gsk/gskroundedrectprivate.h> |
24 | #include <string.h> |
25 | |
26 | #include "gskgluniformstateprivate.h" |
27 | |
28 | static const guint8 uniform_sizes[] = { |
29 | 0, |
30 | |
31 | sizeof (Uniform1f), |
32 | sizeof (Uniform2f), |
33 | sizeof (Uniform3f), |
34 | sizeof (Uniform4f), |
35 | |
36 | sizeof (Uniform1f), |
37 | sizeof (Uniform2f), |
38 | sizeof (Uniform3f), |
39 | sizeof (Uniform4f), |
40 | |
41 | sizeof (Uniform1i), |
42 | sizeof (Uniform2i), |
43 | sizeof (Uniform3i), |
44 | sizeof (Uniform4i), |
45 | |
46 | sizeof (Uniform1ui), |
47 | |
48 | sizeof (guint), |
49 | |
50 | sizeof (graphene_matrix_t), |
51 | sizeof (GskRoundedRect), |
52 | sizeof (GdkRGBA), |
53 | |
54 | 0, |
55 | }; |
56 | |
57 | GskGLUniformState * |
58 | gsk_gl_uniform_state_new (void) |
59 | { |
60 | GskGLUniformState *state; |
61 | |
62 | state = g_atomic_rc_box_new0 (GskGLUniformState); |
63 | state->programs = g_hash_table_new_full (NULL, NULL, NULL, value_destroy_func: g_free); |
64 | state->values_len = 4096; |
65 | state->values_pos = 0; |
66 | state->values_buf = g_malloc (n_bytes: 4096); |
67 | |
68 | memset (s: state->apply_hash, c: 0, n: sizeof state->apply_hash); |
69 | |
70 | return g_steal_pointer (&state); |
71 | } |
72 | |
73 | GskGLUniformState * |
74 | gsk_gl_uniform_state_ref (GskGLUniformState *state) |
75 | { |
76 | return g_atomic_rc_box_acquire (state); |
77 | } |
78 | |
79 | static void |
80 | gsk_gl_uniform_state_finalize (gpointer data) |
81 | { |
82 | GskGLUniformState *state = data; |
83 | |
84 | g_clear_pointer (&state->programs, g_hash_table_unref); |
85 | g_clear_pointer (&state->values_buf, g_free); |
86 | } |
87 | |
88 | void |
89 | gsk_gl_uniform_state_unref (GskGLUniformState *state) |
90 | { |
91 | g_atomic_rc_box_release_full (mem_block: state, clear_func: gsk_gl_uniform_state_finalize); |
92 | } |
93 | |
94 | gpointer |
95 | gsk_gl_uniform_state_init_value (GskGLUniformState *state, |
96 | GskGLUniformProgram *program, |
97 | GskGLUniformFormat format, |
98 | guint array_count, |
99 | guint key, |
100 | GskGLUniformMapping **infoptr) |
101 | { |
102 | GskGLUniformMapping *mapping; |
103 | guint offset; |
104 | |
105 | g_assert (state != NULL); |
106 | g_assert (array_count < 32); |
107 | g_assert ((int)format >= 0 && format < GSK_GL_UNIFORM_FORMAT_LAST); |
108 | g_assert (format > 0); |
109 | g_assert (program != NULL); |
110 | g_assert (key < program->n_mappings); |
111 | |
112 | mapping = &program->mappings[key]; |
113 | |
114 | if (mapping->location == -1) |
115 | { |
116 | *infoptr = NULL; |
117 | return NULL; |
118 | } |
119 | |
120 | if G_LIKELY (format == mapping->info.format) |
121 | { |
122 | if G_LIKELY (array_count <= mapping->info.array_count) |
123 | { |
124 | *infoptr = mapping; |
125 | return GSK_GL_UNIFORM_VALUE (state->values_buf, mapping->info.offset); |
126 | } |
127 | |
128 | /* We found the uniform, but there is not enough space for the |
129 | * amount that was requested. Instead, allocate new space and |
130 | * set the value to "initial" so that the caller just writes |
131 | * over the previous value. |
132 | * |
133 | * This can happen when using dynamic array lengths like the |
134 | * "n_color_stops" in gradient shaders. |
135 | */ |
136 | goto setup_info; |
137 | } |
138 | else if (mapping->info.format == 0) |
139 | { |
140 | goto setup_info; |
141 | } |
142 | else |
143 | { |
144 | g_critical ("Attempt to access uniform with different type of value " |
145 | "than it was initialized with. Program %u Location %u. " |
146 | "Was %d now %d (array length %d now %d)." , |
147 | program->program_id, key, mapping->info.format, format, |
148 | mapping->info.array_count, array_count); |
149 | *infoptr = NULL; |
150 | return NULL; |
151 | } |
152 | |
153 | setup_info: |
154 | |
155 | gsk_gl_uniform_state_realloc (state, |
156 | size: uniform_sizes[format] * MAX (1, array_count), |
157 | offset: &offset); |
158 | |
159 | /* we have 21 bits for offset */ |
160 | g_assert (offset < (1 << GSK_GL_UNIFORM_OFFSET_BITS)); |
161 | |
162 | mapping->info.format = format; |
163 | mapping->info.offset = offset; |
164 | mapping->info.array_count = array_count; |
165 | mapping->info.initial = TRUE; |
166 | mapping->stamp = 0; |
167 | |
168 | *infoptr = mapping; |
169 | |
170 | return GSK_GL_UNIFORM_VALUE (state->values_buf, mapping->info.offset); |
171 | } |
172 | |
173 | void |
174 | gsk_gl_uniform_state_end_frame (GskGLUniformState *state) |
175 | { |
176 | GHashTableIter iter; |
177 | GskGLUniformProgram *program; |
178 | guint allocator = 0; |
179 | |
180 | g_return_if_fail (state != NULL); |
181 | |
182 | /* After a frame finishes, we want to remove all our copies of uniform |
183 | * data that isn't needed any longer. Since we treat it as uninitialized |
184 | * after this frame (to reset it on first use next frame) we can just |
185 | * discard it but keep an allocation around to reuse. |
186 | */ |
187 | |
188 | g_hash_table_iter_init (iter: &iter, hash_table: state->programs); |
189 | while (g_hash_table_iter_next (iter: &iter, NULL, value: (gpointer *)&program)) |
190 | { |
191 | for (guint j = 0; j < program->n_mappings; j++) |
192 | { |
193 | GskGLUniformMapping *mapping = &program->mappings[j]; |
194 | guint size; |
195 | |
196 | /* Skip unused uniform mappings */ |
197 | if (mapping->info.format == 0 || mapping->location == -1) |
198 | continue; |
199 | |
200 | /* Calculate how much size is needed for the uniform, including arrays */ |
201 | size = uniform_sizes[mapping->info.format] * MAX (1, mapping->info.array_count); |
202 | |
203 | /* Adjust alignment for value */ |
204 | allocator += gsk_gl_uniform_state_align (current_pos: allocator, size); |
205 | |
206 | /* Offset is in slots of 4 bytes */ |
207 | mapping->info.offset = allocator / 4; |
208 | mapping->info.initial = TRUE; |
209 | mapping->stamp = 0; |
210 | |
211 | /* Now advance for this items data */ |
212 | allocator += size; |
213 | } |
214 | } |
215 | |
216 | state->values_pos = allocator; |
217 | |
218 | /* It can happen that our space requirements grow due to |
219 | * difference in order increasing padding. As a pragmatic |
220 | * solution to this, just increase the allocation to cover |
221 | * the predefined mappins. |
222 | */ |
223 | if (allocator > state->values_len) |
224 | { |
225 | while (allocator > state->values_len) |
226 | state->values_len *= 2; |
227 | state->values_buf = g_realloc (mem: state->values_buf, n_bytes: state->values_len); |
228 | } |
229 | |
230 | memset (s: state->apply_hash, c: 0, n: sizeof state->apply_hash); |
231 | } |
232 | |
233 | gsize |
234 | gsk_gl_uniform_format_size (GskGLUniformFormat format) |
235 | { |
236 | g_assert (format > 0); |
237 | g_assert (format < GSK_GL_UNIFORM_FORMAT_LAST); |
238 | |
239 | return uniform_sizes[format]; |
240 | } |
241 | |
242 | GskGLUniformProgram * |
243 | gsk_gl_uniform_state_get_program (GskGLUniformState *state, |
244 | guint program, |
245 | const GskGLUniformMapping *mappings, |
246 | guint n_mappings) |
247 | { |
248 | GskGLUniformProgram *ret; |
249 | |
250 | g_return_val_if_fail (state != NULL, NULL); |
251 | g_return_val_if_fail (program > 0, NULL); |
252 | g_return_val_if_fail (program < G_MAXUINT, NULL); |
253 | g_return_val_if_fail (n_mappings <= G_N_ELEMENTS (ret->mappings), NULL); |
254 | |
255 | ret = g_hash_table_lookup (hash_table: state->programs, GUINT_TO_POINTER (program)); |
256 | |
257 | if (ret == NULL) |
258 | { |
259 | ret = g_new0 (GskGLUniformProgram, 1); |
260 | ret->program_id = program; |
261 | ret->n_mappings = n_mappings; |
262 | |
263 | memcpy (dest: ret->mappings, src: mappings, n: n_mappings * sizeof *mappings); |
264 | |
265 | g_hash_table_insert (hash_table: state->programs, GUINT_TO_POINTER (program), value: ret); |
266 | } |
267 | |
268 | return ret; |
269 | } |
270 | |