1 | /* gskglprogram.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 "gskglcommandqueueprivate.h" |
24 | #include "gskglprogramprivate.h" |
25 | #include "gskgluniformstateprivate.h" |
26 | |
27 | G_DEFINE_TYPE (GskGLProgram, gsk_gl_program, G_TYPE_OBJECT) |
28 | |
29 | GskGLProgram * |
30 | gsk_gl_program_new (GskGLDriver *driver, |
31 | const char *name, |
32 | int program_id) |
33 | { |
34 | GskGLProgram *self; |
35 | |
36 | g_return_val_if_fail (GSK_IS_GL_DRIVER (driver), NULL); |
37 | g_return_val_if_fail (program_id >= -1, NULL); |
38 | |
39 | self = g_object_new (GSK_TYPE_GL_PROGRAM, NULL); |
40 | self->id = program_id; |
41 | self->name = g_strdup (str: name); |
42 | self->driver = g_object_ref (driver); |
43 | self->n_mappings = 0; |
44 | |
45 | return self; |
46 | } |
47 | |
48 | static void |
49 | gsk_gl_program_finalize (GObject *object) |
50 | { |
51 | GskGLProgram *self = (GskGLProgram *)object; |
52 | |
53 | if (self->id >= 0) |
54 | g_warning ("Leaking GLSL program %d (%s)" , |
55 | self->id, |
56 | self->name ? self->name : "" ); |
57 | |
58 | g_clear_pointer (&self->name, g_free); |
59 | g_clear_object (&self->driver); |
60 | |
61 | G_OBJECT_CLASS (gsk_gl_program_parent_class)->finalize (object); |
62 | } |
63 | |
64 | static void |
65 | gsk_gl_program_class_init (GskGLProgramClass *klass) |
66 | { |
67 | GObjectClass *object_class = G_OBJECT_CLASS (klass); |
68 | |
69 | object_class->finalize = gsk_gl_program_finalize; |
70 | } |
71 | |
72 | static void |
73 | gsk_gl_program_init (GskGLProgram *self) |
74 | { |
75 | self->id = -1; |
76 | |
77 | for (guint i = 0; i < G_N_ELEMENTS (self->mappings); i++) |
78 | self->mappings[i].location = -1; |
79 | } |
80 | |
81 | /** |
82 | * gsk_gl_program_add_uniform: |
83 | * @self: a `GskGLProgram` |
84 | * @name: the name of the uniform such as "u_source" |
85 | * @key: the identifier to use for the uniform |
86 | * |
87 | * This method will create a mapping between @key and the location |
88 | * of the uniform on the GPU. This simplifies calling code to not |
89 | * need to know where the uniform location is and only register it |
90 | * when creating the program. |
91 | * |
92 | * You might use this with an enum of all your uniforms for the |
93 | * program and then register each of them like: |
94 | * |
95 | * ``` |
96 | * gsk_gl_program_add_uniform (program, "u_source", UNIFORM_SOURCE); |
97 | * ``` |
98 | * |
99 | * That allows you to set values for the program with something |
100 | * like the following: |
101 | * |
102 | * ``` |
103 | * gsk_gl_program_set_uniform1i (program, UNIFORM_SOURCE, 1); |
104 | * ``` |
105 | * |
106 | * Returns: %TRUE if the uniform was found; otherwise %FALSE |
107 | */ |
108 | gboolean |
109 | gsk_gl_program_add_uniform (GskGLProgram *self, |
110 | const char *name, |
111 | guint key) |
112 | { |
113 | GLint location; |
114 | |
115 | g_return_val_if_fail (GSK_IS_GL_PROGRAM (self), FALSE); |
116 | g_return_val_if_fail (name != NULL, FALSE); |
117 | g_return_val_if_fail (key < G_N_ELEMENTS (self->mappings), FALSE); |
118 | |
119 | location = glGetUniformLocation (self->id, name); |
120 | |
121 | /* Register the information even if unused */ |
122 | self->mappings[key].name = g_intern_string (string: name); |
123 | self->mappings[key].location = location; |
124 | if (key >= self->n_mappings) |
125 | self->n_mappings = key + 1; |
126 | |
127 | #if 0 |
128 | g_print ("program [%d] %s uniform %s [%u of %u] at location %d.\n" , |
129 | self->id, self->name, name, key, self->n_mappings, location); |
130 | #endif |
131 | |
132 | return location > -1; |
133 | } |
134 | |
135 | /** |
136 | * gsk_gl_program_delete: |
137 | * @self: a `GskGLProgram` |
138 | * |
139 | * Deletes the GLSL program. |
140 | */ |
141 | void |
142 | gsk_gl_program_delete (GskGLProgram *self) |
143 | { |
144 | g_return_if_fail (GSK_IS_GL_PROGRAM (self)); |
145 | g_return_if_fail (self->driver->command_queue != NULL); |
146 | |
147 | gsk_gl_command_queue_delete_program (self: self->driver->command_queue, program_id: self->id); |
148 | self->id = -1; |
149 | } |
150 | |
151 | /** |
152 | * gsk_gl_program_uniforms_added: |
153 | * @self: a `GskGLProgram` |
154 | * @has_attachments: if any uniform is for a bind/texture attachment |
155 | * |
156 | * This function should be called after all of the uniforms ahve |
157 | * been added with gsk_gl_program_add_uniform(). |
158 | * |
159 | * This function will setup the uniform state so that the program |
160 | * has fast access to the data buffers without as many lookups at |
161 | * runtime for comparison data. |
162 | */ |
163 | void |
164 | gsk_gl_program_uniforms_added (GskGLProgram *self, |
165 | gboolean has_attachments) |
166 | { |
167 | g_return_if_fail (GSK_IS_GL_PROGRAM (self)); |
168 | g_return_if_fail (self->uniforms == NULL); |
169 | |
170 | self->uniforms = self->driver->command_queue->uniforms; |
171 | self->program_info = gsk_gl_uniform_state_get_program (state: self->uniforms, program: self->id, mappings: self->mappings, n_mappings: self->n_mappings); |
172 | self->program_info->has_attachments = has_attachments; |
173 | } |
174 | |