1 #include "config.h"
2 
3 #include "gskglshaderbuilderprivate.h"
4 
5 #include "gskdebugprivate.h"
6 
7 #include <gdk/gdk.h>
8 #include <epoxy/gl.h>
9 
10 void
gsk_gl_shader_builder_init(GskGLShaderBuilder * self,const char * common_preamble_resource_path,const char * vs_preamble_resource_path,const char * fs_preamble_resource_path)11 gsk_gl_shader_builder_init (GskGLShaderBuilder *self,
12                             const char         *common_preamble_resource_path,
13                             const char         *vs_preamble_resource_path,
14                             const char         *fs_preamble_resource_path)
15 {
16   memset (self, 0, sizeof (*self));
17 
18   self->preamble = g_resources_lookup_data (common_preamble_resource_path, 0, NULL);
19   self->vs_preamble = g_resources_lookup_data (vs_preamble_resource_path, 0, NULL);
20   self->fs_preamble = g_resources_lookup_data (fs_preamble_resource_path, 0, NULL);
21 
22   g_assert (self->preamble);
23   g_assert (self->vs_preamble);
24   g_assert (self->fs_preamble);
25 }
26 
27 void
gsk_gl_shader_builder_finish(GskGLShaderBuilder * self)28 gsk_gl_shader_builder_finish (GskGLShaderBuilder *self)
29 {
30   g_bytes_unref (self->preamble);
31   g_bytes_unref (self->vs_preamble);
32   g_bytes_unref (self->fs_preamble);
33 }
34 
35 void
gsk_gl_shader_builder_set_glsl_version(GskGLShaderBuilder * self,int version)36 gsk_gl_shader_builder_set_glsl_version (GskGLShaderBuilder *self,
37                                         int                 version)
38 {
39   self->version = version;
40 }
41 
42 static void
prepend_line_numbers(char * code,GString * s)43 prepend_line_numbers (char    *code,
44                       GString *s)
45 {
46   char *p;
47   int line;
48 
49   p = code;
50   line = 1;
51   while (*p)
52     {
53       char *end = strchr (p, '\n');
54       if (end)
55         end = end + 1; /* Include newline */
56       else
57         end = p + strlen (p);
58 
59       g_string_append_printf (s, "%3d| ", line++);
60       g_string_append_len (s, p, end - p);
61 
62       p = end;
63     }
64 }
65 
66 static gboolean
check_shader_error(int shader_id,int shader_type,const char * resource_path,GError ** error)67 check_shader_error (int          shader_id,
68                     int          shader_type,
69                     const char  *resource_path,
70                     GError     **error)
71 {
72   int status;
73   int log_len;
74   char *buffer;
75   int code_len;
76   char *code;
77   GString *s;
78 
79   glGetShaderiv (shader_id, GL_COMPILE_STATUS, &status);
80 
81   if (G_LIKELY (status == GL_TRUE))
82     return TRUE;
83 
84   glGetShaderiv (shader_id, GL_INFO_LOG_LENGTH, &log_len);
85   buffer = g_malloc0 (log_len + 1);
86   glGetShaderInfoLog (shader_id, log_len, NULL, buffer);
87 
88   glGetShaderiv (shader_id, GL_SHADER_SOURCE_LENGTH, &code_len);
89   code = g_malloc0 (code_len + 1);
90   glGetShaderSource (shader_id, code_len, NULL, code);
91 
92   s = g_string_new ("");
93   prepend_line_numbers (code, s);
94 
95   g_set_error (error, GDK_GL_ERROR, GDK_GL_ERROR_COMPILATION_FAILED,
96                "Compilation failure in %s shader %s.\nSource Code:\n%s\n\nError Message:\n%s\n\n",
97                (shader_type == GL_FRAGMENT_SHADER ? "fragment" : "vertex"),
98                resource_path,
99                s->str,
100                buffer);
101 
102   g_string_free (s, TRUE);
103   g_free (buffer);
104   g_free (code);
105 
106   return FALSE;
107 }
108 
109 static void
print_shader_info(const char * prefix,int shader_id,const char * resource_path)110 print_shader_info (const char *prefix,
111                    int         shader_id,
112                    const char *resource_path)
113 {
114   if (GSK_DEBUG_CHECK(SHADERS))
115     {
116       int code_len;
117       char *code;
118       GString *s;
119 
120       glGetShaderiv (shader_id, GL_SHADER_SOURCE_LENGTH, &code_len);
121       code = g_malloc0 (code_len + 1);
122       glGetShaderSource (shader_id, code_len, NULL, code);
123 
124       s = g_string_new ("");
125       prepend_line_numbers (code, s);
126 
127       g_message ("%s %d, %s:\n%s", prefix, shader_id, resource_path, s->str);
128       g_string_free (s,  TRUE);
129       g_free (code);
130     }
131 }
132 
133 int
gsk_gl_shader_builder_create_program(GskGLShaderBuilder * self,const char * resource_path,const char * extra_fragment_snippet,gsize extra_fragment_length,GError ** error)134 gsk_gl_shader_builder_create_program (GskGLShaderBuilder  *self,
135                                       const char          *resource_path,
136                                       const char          *extra_fragment_snippet,
137                                       gsize                extra_fragment_length,
138                                       GError             **error)
139 {
140 
141   GBytes *source_bytes = g_resources_lookup_data (resource_path, 0, NULL);
142   char version_buffer[64];
143   const char *source;
144   const char *vertex_shader_start;
145   const char *fragment_shader_start;
146   int vertex_id;
147   int fragment_id;
148   int program_id = -1;
149   int status;
150 
151   g_assert (source_bytes);
152 
153   source = g_bytes_get_data (source_bytes, NULL);
154   vertex_shader_start = strstr (source, "VERTEX_SHADER");
155   fragment_shader_start = strstr (source, "FRAGMENT_SHADER");
156 
157   g_assert (vertex_shader_start);
158   g_assert (fragment_shader_start);
159 
160   /* They both start at the next newline */
161   vertex_shader_start = strstr (vertex_shader_start, "\n");
162   fragment_shader_start = strstr (fragment_shader_start, "\n");
163 
164   g_snprintf (version_buffer, sizeof (version_buffer),
165               "#version %d\n", self->version);
166 
167   vertex_id = glCreateShader (GL_VERTEX_SHADER);
168   glShaderSource (vertex_id, 8,
169                   (const char *[]) {
170                     version_buffer,
171                     self->debugging ? "#define GSK_DEBUG 1\n" : "",
172                     self->legacy ? "#define GSK_LEGACY 1\n" : "",
173                     self->gl3 ? "#define GSK_GL3 1\n" : "",
174                     self->gles ? "#define GSK_GLES 1\n" : "",
175                     g_bytes_get_data (self->preamble, NULL),
176                     g_bytes_get_data (self->vs_preamble, NULL),
177                     vertex_shader_start
178                   },
179                   (int[]) {
180                     -1,
181                     -1,
182                     -1,
183                     -1,
184                     -1,
185                     -1,
186                     -1,
187                     fragment_shader_start - vertex_shader_start
188                   });
189   glCompileShader (vertex_id);
190 
191   if (!check_shader_error (vertex_id, GL_VERTEX_SHADER, resource_path, error))
192     {
193       glDeleteShader (vertex_id);
194       goto out;
195     }
196 
197   print_shader_info ("Vertex shader", vertex_id, resource_path);
198 
199   fragment_id = glCreateShader (GL_FRAGMENT_SHADER);
200   glShaderSource (fragment_id, 9,
201                   (const char *[]) {
202                     version_buffer,
203                     self->debugging ? "#define GSK_DEBUG 1\n" : "",
204                     self->legacy ? "#define GSK_LEGACY 1\n" : "",
205                     self->gl3 ? "#define GSK_GL3 1\n" : "",
206                     self->gles ? "#define GSK_GLES 1\n" : "",
207                     g_bytes_get_data (self->preamble, NULL),
208                     g_bytes_get_data (self->fs_preamble, NULL),
209                     fragment_shader_start,
210                     extra_fragment_snippet ? extra_fragment_snippet : ""
211                   },
212                   (int[]) {
213                     -1,
214                     -1,
215                     -1,
216                     -1,
217                     -1,
218                     -1,
219                     -1,
220                     -1,
221                     extra_fragment_length,
222                   });
223   glCompileShader (fragment_id);
224 
225   if (!check_shader_error (fragment_id, GL_FRAGMENT_SHADER, resource_path, error))
226     {
227       glDeleteShader (fragment_id);
228       goto out;
229     }
230 
231   print_shader_info ("Fragment shader", fragment_id, resource_path);
232 
233   program_id = glCreateProgram ();
234   glAttachShader (program_id, vertex_id);
235   glAttachShader (program_id, fragment_id);
236   glBindAttribLocation (program_id, 0, "aPosition");
237   glBindAttribLocation (program_id, 1, "aUv");
238   glLinkProgram (program_id);
239   glDetachShader (program_id, vertex_id);
240   glDetachShader (program_id, fragment_id);
241 
242   glGetProgramiv (program_id, GL_LINK_STATUS, &status);
243   if (status == GL_FALSE)
244     {
245       char *buffer = NULL;
246       int log_len = 0;
247 
248       glGetProgramiv (program_id, GL_INFO_LOG_LENGTH, &log_len);
249 
250       buffer = g_malloc0 (log_len + 1);
251       glGetProgramInfoLog (program_id, log_len, NULL, buffer);
252 
253       g_warning ("Linking failure in shader:\n%s", buffer);
254       g_set_error (error, GDK_GL_ERROR, GDK_GL_ERROR_LINK_FAILED,
255                    "Linking failure in shader: %s", buffer);
256 
257       g_free (buffer);
258 
259       glDeleteProgram (program_id);
260       program_id = -1;
261     }
262 
263   glDeleteShader (vertex_id);
264   glDeleteShader (fragment_id);
265 
266 out:
267   g_bytes_unref (source_bytes);
268 
269   return program_id;
270 }
271 
272