forked from AuroraMiddleware/gtk
9460d0131f
Almost always the source is created by combining various sources, which means the line numbers in the error messages are hard to use. Adding the line numbers to the source in the error message helps with this.
228 lines
6.2 KiB
C
228 lines
6.2 KiB
C
#include "config.h"
|
|
|
|
#include "gskglshaderbuilderprivate.h"
|
|
|
|
#include "gskdebugprivate.h"
|
|
|
|
#include <gdk/gdk.h>
|
|
#include <epoxy/gl.h>
|
|
|
|
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)
|
|
{
|
|
memset (self, 0, sizeof (*self));
|
|
|
|
self->preamble = g_resources_lookup_data (common_preamble_resource_path, 0, NULL);
|
|
self->vs_preamble = g_resources_lookup_data (vs_preamble_resource_path, 0, NULL);
|
|
self->fs_preamble = g_resources_lookup_data (fs_preamble_resource_path, 0, NULL);
|
|
|
|
g_assert (self->preamble);
|
|
g_assert (self->vs_preamble);
|
|
g_assert (self->fs_preamble);
|
|
}
|
|
|
|
void
|
|
gsk_gl_shader_builder_finish (GskGLShaderBuilder *self)
|
|
{
|
|
g_bytes_unref (self->preamble);
|
|
g_bytes_unref (self->vs_preamble);
|
|
g_bytes_unref (self->fs_preamble);
|
|
}
|
|
|
|
void
|
|
gsk_gl_shader_builder_set_glsl_version (GskGLShaderBuilder *self,
|
|
int version)
|
|
{
|
|
self->version = version;
|
|
}
|
|
|
|
static gboolean
|
|
check_shader_error (int shader_id,
|
|
GError **error)
|
|
{
|
|
int status;
|
|
int log_len;
|
|
char *buffer;
|
|
int code_len;
|
|
int line;
|
|
char *code, *p;
|
|
GString *s;
|
|
|
|
glGetShaderiv (shader_id, GL_COMPILE_STATUS, &status);
|
|
|
|
if (G_LIKELY (status == GL_TRUE))
|
|
return TRUE;
|
|
|
|
glGetShaderiv (shader_id, GL_INFO_LOG_LENGTH, &log_len);
|
|
buffer = g_malloc0 (log_len + 1);
|
|
glGetShaderInfoLog (shader_id, log_len, NULL, buffer);
|
|
|
|
glGetShaderiv (shader_id, GL_SHADER_SOURCE_LENGTH, &code_len);
|
|
code = g_malloc0 (code_len + 1);
|
|
glGetShaderSource (shader_id, code_len, NULL, code);
|
|
|
|
s = g_string_new ("");
|
|
p = code;
|
|
line = 1;
|
|
while (*p)
|
|
{
|
|
char *end = strchr (p, '\n');
|
|
if (end)
|
|
end = end + 1; /* Include newline */
|
|
else
|
|
end = p + strlen (p);
|
|
|
|
g_string_append_printf (s, "%3d| ", line++);
|
|
g_string_append_len (s, p, end - p);
|
|
|
|
p = end;
|
|
}
|
|
|
|
g_set_error (error, GDK_GL_ERROR, GDK_GL_ERROR_COMPILATION_FAILED,
|
|
"Compilation failure in shader.\nError message: %s\n\nSource code:\n%s\n\n",
|
|
buffer,
|
|
s->str);
|
|
|
|
g_string_free (s, TRUE);
|
|
g_free (buffer);
|
|
g_free (code);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
gsk_gl_shader_builder_create_program (GskGLShaderBuilder *self,
|
|
const char *resource_path,
|
|
GError **error)
|
|
{
|
|
|
|
GBytes *source_bytes = g_resources_lookup_data (resource_path, 0, NULL);
|
|
char version_buffer[64];
|
|
const char *source;
|
|
const char *vertex_shader_start;
|
|
const char *fragment_shader_start;
|
|
int vertex_id;
|
|
int fragment_id;
|
|
int program_id = -1;
|
|
int status;
|
|
|
|
g_assert (source_bytes);
|
|
|
|
source = g_bytes_get_data (source_bytes, NULL);
|
|
vertex_shader_start = strstr (source, "VERTEX_SHADER");
|
|
fragment_shader_start = strstr (source, "FRAGMENT_SHADER");
|
|
|
|
g_assert (vertex_shader_start);
|
|
g_assert (fragment_shader_start);
|
|
|
|
/* They both start at the next newline */
|
|
vertex_shader_start = strstr (vertex_shader_start, "\n");
|
|
fragment_shader_start = strstr (fragment_shader_start, "\n");
|
|
|
|
g_snprintf (version_buffer, sizeof (version_buffer),
|
|
"#version %d\n", self->version);
|
|
|
|
vertex_id = glCreateShader (GL_VERTEX_SHADER);
|
|
glShaderSource (vertex_id, 8,
|
|
(const char *[]) {
|
|
version_buffer,
|
|
self->debugging ? "#define GSK_DEBUG 1\n" : "",
|
|
self->legacy ? "#define GSK_LEGACY 1\n" : "",
|
|
self->gl3 ? "#define GSK_GL3 1\n" : "",
|
|
self->gles ? "#define GSK_GLES 1\n" : "",
|
|
g_bytes_get_data (self->preamble, NULL),
|
|
g_bytes_get_data (self->vs_preamble, NULL),
|
|
vertex_shader_start
|
|
},
|
|
(int[]) {
|
|
-1,
|
|
-1,
|
|
-1,
|
|
-1,
|
|
-1,
|
|
-1,
|
|
-1,
|
|
fragment_shader_start - vertex_shader_start
|
|
});
|
|
glCompileShader (vertex_id);
|
|
|
|
if (!check_shader_error (vertex_id, error))
|
|
{
|
|
glDeleteShader (vertex_id);
|
|
goto out;
|
|
}
|
|
|
|
fragment_id = glCreateShader (GL_FRAGMENT_SHADER);
|
|
glShaderSource (fragment_id, 8,
|
|
(const char *[]) {
|
|
version_buffer,
|
|
self->debugging ? "#define GSK_DEBUG 1\n" : "",
|
|
self->legacy ? "#define GSK_LEGACY 1\n" : "",
|
|
self->gl3 ? "#define GSK_GL3 1\n" : "",
|
|
self->gles ? "#define GSK_GLES 1\n" : "",
|
|
g_bytes_get_data (self->preamble, NULL),
|
|
g_bytes_get_data (self->fs_preamble, NULL),
|
|
fragment_shader_start
|
|
},
|
|
(int[]) {
|
|
-1,
|
|
-1,
|
|
-1,
|
|
-1,
|
|
-1,
|
|
-1,
|
|
-1,
|
|
-1,
|
|
});
|
|
glCompileShader (fragment_id);
|
|
|
|
if (!check_shader_error (fragment_id, error))
|
|
{
|
|
glDeleteShader (fragment_id);
|
|
goto out;
|
|
}
|
|
|
|
program_id = glCreateProgram ();
|
|
glAttachShader (program_id, vertex_id);
|
|
glAttachShader (program_id, fragment_id);
|
|
glLinkProgram (program_id);
|
|
|
|
glGetProgramiv (program_id, GL_LINK_STATUS, &status);
|
|
if (status == GL_FALSE)
|
|
{
|
|
char *buffer = NULL;
|
|
int log_len = 0;
|
|
|
|
glGetProgramiv (program_id, GL_INFO_LOG_LENGTH, &log_len);
|
|
|
|
buffer = g_malloc0 (log_len + 1);
|
|
glGetProgramInfoLog (program_id, log_len, NULL, buffer);
|
|
|
|
g_warning ("Linking failure in shader:\n%s", buffer);
|
|
g_set_error (error, GDK_GL_ERROR, GDK_GL_ERROR_LINK_FAILED,
|
|
"Linking failure in shader: %s", buffer);
|
|
|
|
g_free (buffer);
|
|
|
|
glDeleteProgram (program_id);
|
|
program_id = -1;
|
|
|
|
goto out;
|
|
}
|
|
|
|
glDetachShader (program_id, vertex_id);
|
|
glDeleteShader (vertex_id);
|
|
|
|
glDetachShader (program_id, fragment_id);
|
|
glDeleteShader (fragment_id);
|
|
|
|
out:
|
|
g_bytes_unref (source_bytes);
|
|
|
|
return program_id;
|
|
}
|
|
|