forked from AuroraMiddleware/gtk
679 lines
19 KiB
C
679 lines
19 KiB
C
/* gsknglcompiler.c
|
|
*
|
|
* Copyright 2020 Christian Hergert <chergert@redhat.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <gsk/gskdebugprivate.h>
|
|
#include <gio/gio.h>
|
|
#include <string.h>
|
|
|
|
#include "gsknglcommandqueueprivate.h"
|
|
#include "gsknglcompilerprivate.h"
|
|
#include "gsknglprogramprivate.h"
|
|
|
|
#define SHADER_VERSION_GLES 100
|
|
#define SHADER_VERSION_GL2_LEGACY 110
|
|
#define SHADER_VERSION_GL3_LEGACY 130
|
|
#define SHADER_VERSION_GL3 150
|
|
|
|
struct _GskNglCompiler
|
|
{
|
|
GObject parent_instance;
|
|
|
|
GskNglDriver *driver;
|
|
|
|
GBytes *all_preamble;
|
|
GBytes *fragment_preamble;
|
|
GBytes *vertex_preamble;
|
|
GBytes *fragment_source;
|
|
GBytes *fragment_suffix;
|
|
GBytes *vertex_source;
|
|
GBytes *vertex_suffix;
|
|
|
|
GArray *attrib_locations;
|
|
|
|
int glsl_version;
|
|
|
|
guint gl3 : 1;
|
|
guint gles : 1;
|
|
guint legacy : 1;
|
|
guint debug_shaders : 1;
|
|
};
|
|
|
|
typedef struct _GskNglProgramAttrib
|
|
{
|
|
const char *name;
|
|
guint location;
|
|
} GskNglProgramAttrib;
|
|
|
|
static GBytes *empty_bytes;
|
|
|
|
G_DEFINE_TYPE (GskNglCompiler, gsk_ngl_compiler, G_TYPE_OBJECT)
|
|
|
|
static void
|
|
gsk_ngl_compiler_finalize (GObject *object)
|
|
{
|
|
GskNglCompiler *self = (GskNglCompiler *)object;
|
|
|
|
g_clear_pointer (&self->all_preamble, g_bytes_unref);
|
|
g_clear_pointer (&self->fragment_preamble, g_bytes_unref);
|
|
g_clear_pointer (&self->vertex_preamble, g_bytes_unref);
|
|
g_clear_pointer (&self->vertex_suffix, g_bytes_unref);
|
|
g_clear_pointer (&self->fragment_source, g_bytes_unref);
|
|
g_clear_pointer (&self->fragment_suffix, g_bytes_unref);
|
|
g_clear_pointer (&self->vertex_source, g_bytes_unref);
|
|
g_clear_pointer (&self->attrib_locations, g_array_unref);
|
|
g_clear_object (&self->driver);
|
|
|
|
G_OBJECT_CLASS (gsk_ngl_compiler_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
gsk_ngl_compiler_class_init (GskNglCompilerClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->finalize = gsk_ngl_compiler_finalize;
|
|
|
|
empty_bytes = g_bytes_new (NULL, 0);
|
|
}
|
|
|
|
static void
|
|
gsk_ngl_compiler_init (GskNglCompiler *self)
|
|
{
|
|
self->glsl_version = 150;
|
|
self->attrib_locations = g_array_new (FALSE, FALSE, sizeof (GskNglProgramAttrib));
|
|
self->all_preamble = g_bytes_ref (empty_bytes);
|
|
self->vertex_preamble = g_bytes_ref (empty_bytes);
|
|
self->fragment_preamble = g_bytes_ref (empty_bytes);
|
|
self->vertex_source = g_bytes_ref (empty_bytes);
|
|
self->vertex_suffix = g_bytes_ref (empty_bytes);
|
|
self->fragment_source = g_bytes_ref (empty_bytes);
|
|
self->fragment_suffix = g_bytes_ref (empty_bytes);
|
|
}
|
|
|
|
GskNglCompiler *
|
|
gsk_ngl_compiler_new (GskNglDriver *driver,
|
|
gboolean debug_shaders)
|
|
{
|
|
GskNglCompiler *self;
|
|
GdkGLContext *context;
|
|
|
|
g_return_val_if_fail (GSK_IS_NGL_DRIVER (driver), NULL);
|
|
g_return_val_if_fail (driver->shared_command_queue != NULL, NULL);
|
|
|
|
self = g_object_new (GSK_TYPE_GL_COMPILER, NULL);
|
|
self->driver = g_object_ref (driver);
|
|
self->debug_shaders = !!debug_shaders;
|
|
|
|
context = gsk_ngl_command_queue_get_context (self->driver->shared_command_queue);
|
|
|
|
if (gdk_gl_context_get_use_es (context))
|
|
{
|
|
self->glsl_version = SHADER_VERSION_GLES;
|
|
self->gles = TRUE;
|
|
}
|
|
else if (gdk_gl_context_is_legacy (context))
|
|
{
|
|
int maj, min;
|
|
|
|
gdk_gl_context_get_version (context, &maj, &min);
|
|
|
|
if (maj == 3)
|
|
self->glsl_version = SHADER_VERSION_GL3_LEGACY;
|
|
else
|
|
self->glsl_version = SHADER_VERSION_GL2_LEGACY;
|
|
|
|
self->legacy = TRUE;
|
|
}
|
|
else
|
|
{
|
|
self->glsl_version = SHADER_VERSION_GL3;
|
|
self->gl3 = TRUE;
|
|
}
|
|
|
|
gsk_ngl_command_queue_make_current (self->driver->shared_command_queue);
|
|
|
|
return g_steal_pointer (&self);
|
|
}
|
|
|
|
void
|
|
gsk_ngl_compiler_bind_attribute (GskNglCompiler *self,
|
|
const char *name,
|
|
guint location)
|
|
{
|
|
GskNglProgramAttrib attrib;
|
|
|
|
g_return_if_fail (GSK_IS_NGL_COMPILER (self));
|
|
g_return_if_fail (name != NULL);
|
|
g_return_if_fail (location < 32);
|
|
|
|
attrib.name = g_intern_string (name);
|
|
attrib.location = location;
|
|
|
|
g_array_append_val (self->attrib_locations, attrib);
|
|
}
|
|
|
|
void
|
|
gsk_ngl_compiler_clear_attributes (GskNglCompiler *self)
|
|
{
|
|
g_return_if_fail (GSK_IS_NGL_COMPILER (self));
|
|
|
|
g_array_set_size (self->attrib_locations, 0);
|
|
}
|
|
|
|
void
|
|
gsk_ngl_compiler_set_preamble (GskNglCompiler *self,
|
|
GskNglCompilerKind kind,
|
|
GBytes *preamble_bytes)
|
|
{
|
|
GBytes **loc = NULL;
|
|
|
|
g_return_if_fail (GSK_IS_NGL_COMPILER (self));
|
|
g_return_if_fail (preamble_bytes != NULL);
|
|
|
|
if (kind == GSK_NGL_COMPILER_ALL)
|
|
loc = &self->all_preamble;
|
|
else if (kind == GSK_NGL_COMPILER_FRAGMENT)
|
|
loc = &self->fragment_preamble;
|
|
else if (kind == GSK_NGL_COMPILER_VERTEX)
|
|
loc = &self->vertex_preamble;
|
|
else
|
|
g_return_if_reached ();
|
|
|
|
g_assert (loc != NULL);
|
|
|
|
if (*loc != preamble_bytes)
|
|
{
|
|
g_clear_pointer (loc, g_bytes_unref);
|
|
*loc = preamble_bytes ? g_bytes_ref (preamble_bytes) : NULL;
|
|
}
|
|
}
|
|
|
|
void
|
|
gsk_ngl_compiler_set_preamble_from_resource (GskNglCompiler *self,
|
|
GskNglCompilerKind kind,
|
|
const char *resource_path)
|
|
{
|
|
GError *error = NULL;
|
|
GBytes *bytes;
|
|
|
|
g_return_if_fail (GSK_IS_NGL_COMPILER (self));
|
|
g_return_if_fail (kind == GSK_NGL_COMPILER_ALL ||
|
|
kind == GSK_NGL_COMPILER_VERTEX ||
|
|
kind == GSK_NGL_COMPILER_FRAGMENT);
|
|
g_return_if_fail (resource_path != NULL);
|
|
|
|
bytes = g_resources_lookup_data (resource_path,
|
|
G_RESOURCE_LOOKUP_FLAGS_NONE,
|
|
&error);
|
|
|
|
if (bytes == NULL)
|
|
g_warning ("Cannot set shader from resource: %s", error->message);
|
|
else
|
|
gsk_ngl_compiler_set_preamble (self, kind, bytes);
|
|
|
|
g_clear_pointer (&bytes, g_bytes_unref);
|
|
g_clear_error (&error);
|
|
}
|
|
|
|
void
|
|
gsk_ngl_compiler_set_source (GskNglCompiler *self,
|
|
GskNglCompilerKind kind,
|
|
GBytes *source_bytes)
|
|
{
|
|
GBytes **loc = NULL;
|
|
|
|
g_return_if_fail (GSK_IS_NGL_COMPILER (self));
|
|
g_return_if_fail (kind == GSK_NGL_COMPILER_ALL ||
|
|
kind == GSK_NGL_COMPILER_VERTEX ||
|
|
kind == GSK_NGL_COMPILER_FRAGMENT);
|
|
|
|
if (source_bytes == NULL)
|
|
source_bytes = empty_bytes;
|
|
|
|
/* If kind is ALL, then we need to split the fragment and
|
|
* vertex shaders from the bytes and assign them individually.
|
|
* This safely scans for FRAGMENT_SHADER and VERTEX_SHADER as
|
|
* specified within the GLSL resources. Some care is taken to
|
|
* use GBytes which reference the original bytes instead of
|
|
* copying them.
|
|
*/
|
|
if (kind == GSK_NGL_COMPILER_ALL)
|
|
{
|
|
gsize len = 0;
|
|
const char *source;
|
|
const char *vertex_shader_start;
|
|
const char *fragment_shader_start;
|
|
const char *endpos;
|
|
GBytes *fragment_bytes;
|
|
GBytes *vertex_bytes;
|
|
|
|
g_clear_pointer (&self->fragment_source, g_bytes_unref);
|
|
g_clear_pointer (&self->vertex_source, g_bytes_unref);
|
|
|
|
source = g_bytes_get_data (source_bytes, &len);
|
|
endpos = source + len;
|
|
vertex_shader_start = g_strstr_len (source, len, "VERTEX_SHADER");
|
|
fragment_shader_start = g_strstr_len (source, len, "FRAGMENT_SHADER");
|
|
|
|
if (vertex_shader_start == NULL)
|
|
{
|
|
g_warning ("Failed to locate VERTEX_SHADER in shader source");
|
|
return;
|
|
}
|
|
|
|
if (fragment_shader_start == NULL)
|
|
{
|
|
g_warning ("Failed to locate FRAGMENT_SHADER in shader source");
|
|
return;
|
|
}
|
|
|
|
if (vertex_shader_start > fragment_shader_start)
|
|
{
|
|
g_warning ("VERTEX_SHADER must come before FRAGMENT_SHADER");
|
|
return;
|
|
}
|
|
|
|
/* Locate next newlines */
|
|
while (vertex_shader_start < endpos && vertex_shader_start[0] != '\n')
|
|
vertex_shader_start++;
|
|
while (fragment_shader_start < endpos && fragment_shader_start[0] != '\n')
|
|
fragment_shader_start++;
|
|
|
|
vertex_bytes = g_bytes_new_from_bytes (source_bytes,
|
|
vertex_shader_start - source,
|
|
fragment_shader_start - vertex_shader_start);
|
|
fragment_bytes = g_bytes_new_from_bytes (source_bytes,
|
|
fragment_shader_start - source,
|
|
endpos - fragment_shader_start);
|
|
|
|
gsk_ngl_compiler_set_source (self, GSK_NGL_COMPILER_VERTEX, vertex_bytes);
|
|
gsk_ngl_compiler_set_source (self, GSK_NGL_COMPILER_FRAGMENT, fragment_bytes);
|
|
|
|
g_bytes_unref (fragment_bytes);
|
|
g_bytes_unref (vertex_bytes);
|
|
|
|
return;
|
|
}
|
|
|
|
if (kind == GSK_NGL_COMPILER_FRAGMENT)
|
|
loc = &self->fragment_source;
|
|
else if (kind == GSK_NGL_COMPILER_VERTEX)
|
|
loc = &self->vertex_source;
|
|
else
|
|
g_return_if_reached ();
|
|
|
|
if (*loc != source_bytes)
|
|
{
|
|
g_clear_pointer (loc, g_bytes_unref);
|
|
*loc = g_bytes_ref (source_bytes);
|
|
}
|
|
}
|
|
|
|
void
|
|
gsk_ngl_compiler_set_source_from_resource (GskNglCompiler *self,
|
|
GskNglCompilerKind kind,
|
|
const char *resource_path)
|
|
{
|
|
GError *error = NULL;
|
|
GBytes *bytes;
|
|
|
|
g_return_if_fail (GSK_IS_NGL_COMPILER (self));
|
|
g_return_if_fail (kind == GSK_NGL_COMPILER_ALL ||
|
|
kind == GSK_NGL_COMPILER_VERTEX ||
|
|
kind == GSK_NGL_COMPILER_FRAGMENT);
|
|
g_return_if_fail (resource_path != NULL);
|
|
|
|
bytes = g_resources_lookup_data (resource_path,
|
|
G_RESOURCE_LOOKUP_FLAGS_NONE,
|
|
&error);
|
|
|
|
if (bytes == NULL)
|
|
g_warning ("Cannot set shader from resource: %s", error->message);
|
|
else
|
|
gsk_ngl_compiler_set_source (self, kind, bytes);
|
|
|
|
g_clear_pointer (&bytes, g_bytes_unref);
|
|
g_clear_error (&error);
|
|
}
|
|
|
|
void
|
|
gsk_ngl_compiler_set_suffix (GskNglCompiler *self,
|
|
GskNglCompilerKind kind,
|
|
GBytes *suffix_bytes)
|
|
{
|
|
GBytes **loc;
|
|
|
|
g_return_if_fail (GSK_IS_NGL_COMPILER (self));
|
|
g_return_if_fail (kind == GSK_NGL_COMPILER_VERTEX ||
|
|
kind == GSK_NGL_COMPILER_FRAGMENT);
|
|
g_return_if_fail (suffix_bytes != NULL);
|
|
|
|
if (suffix_bytes == NULL)
|
|
suffix_bytes = empty_bytes;
|
|
|
|
if (kind == GSK_NGL_COMPILER_FRAGMENT)
|
|
loc = &self->fragment_suffix;
|
|
else if (kind == GSK_NGL_COMPILER_VERTEX)
|
|
loc = &self->vertex_suffix;
|
|
else
|
|
g_return_if_reached ();
|
|
|
|
if (*loc != suffix_bytes)
|
|
{
|
|
g_clear_pointer (loc, g_bytes_unref);
|
|
*loc = g_bytes_ref (suffix_bytes);
|
|
}
|
|
}
|
|
|
|
void
|
|
gsk_ngl_compiler_set_suffix_from_resource (GskNglCompiler *self,
|
|
GskNglCompilerKind kind,
|
|
const char *resource_path)
|
|
{
|
|
GError *error = NULL;
|
|
GBytes *bytes;
|
|
|
|
g_return_if_fail (GSK_IS_NGL_COMPILER (self));
|
|
g_return_if_fail (kind == GSK_NGL_COMPILER_VERTEX ||
|
|
kind == GSK_NGL_COMPILER_FRAGMENT);
|
|
g_return_if_fail (resource_path != NULL);
|
|
|
|
bytes = g_resources_lookup_data (resource_path,
|
|
G_RESOURCE_LOOKUP_FLAGS_NONE,
|
|
&error);
|
|
|
|
if (bytes == NULL)
|
|
g_warning ("Cannot set suffix from resource: %s", error->message);
|
|
else
|
|
gsk_ngl_compiler_set_suffix (self, kind, bytes);
|
|
|
|
g_clear_pointer (&bytes, g_bytes_unref);
|
|
g_clear_error (&error);
|
|
}
|
|
|
|
static void
|
|
prepend_line_numbers (char *code,
|
|
GString *s)
|
|
{
|
|
char *p;
|
|
int line;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
check_shader_error (int shader_id,
|
|
GError **error)
|
|
{
|
|
GLint status;
|
|
GLint log_len;
|
|
GLint code_len;
|
|
char *buffer;
|
|
char *code;
|
|
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 ("");
|
|
prepend_line_numbers (code, s);
|
|
|
|
g_set_error (error,
|
|
GDK_GL_ERROR,
|
|
GDK_GL_ERROR_COMPILATION_FAILED,
|
|
"Compilation failure in shader.\n"
|
|
"Source Code: %s\n"
|
|
"\n"
|
|
"Error Message:\n"
|
|
"%s\n"
|
|
"\n",
|
|
s->str,
|
|
buffer);
|
|
|
|
g_string_free (s, TRUE);
|
|
g_free (buffer);
|
|
g_free (code);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
print_shader_info (const char *prefix,
|
|
int shader_id,
|
|
const char *name)
|
|
{
|
|
if (GSK_DEBUG_CHECK(SHADERS))
|
|
{
|
|
int code_len;
|
|
|
|
glGetShaderiv (shader_id, GL_SHADER_SOURCE_LENGTH, &code_len);
|
|
|
|
if (code_len > 0)
|
|
{
|
|
char *code;
|
|
GString *s;
|
|
|
|
code = g_malloc0 (code_len + 1);
|
|
glGetShaderSource (shader_id, code_len, NULL, code);
|
|
|
|
s = g_string_new (NULL);
|
|
prepend_line_numbers (code, s);
|
|
|
|
g_message ("%s %d, %s:\n%s",
|
|
prefix, shader_id,
|
|
name ? name : "unnamed",
|
|
s->str);
|
|
g_string_free (s, TRUE);
|
|
g_free (code);
|
|
}
|
|
}
|
|
}
|
|
|
|
static const char *
|
|
get_shader_string (GBytes *bytes)
|
|
{
|
|
/* 0 length bytes will give us NULL back */
|
|
const char *str = g_bytes_get_data (bytes, NULL);
|
|
return str ? str : "";
|
|
}
|
|
|
|
GskNglProgram *
|
|
gsk_ngl_compiler_compile (GskNglCompiler *self,
|
|
const char *name,
|
|
GError **error)
|
|
{
|
|
char version[32];
|
|
const char *debug = "";
|
|
const char *legacy = "";
|
|
const char *gl3 = "";
|
|
const char *gles = "";
|
|
int program_id;
|
|
int vertex_id;
|
|
int fragment_id;
|
|
int status;
|
|
|
|
g_return_val_if_fail (GSK_IS_NGL_COMPILER (self), NULL);
|
|
g_return_val_if_fail (self->all_preamble != NULL, NULL);
|
|
g_return_val_if_fail (self->fragment_preamble != NULL, NULL);
|
|
g_return_val_if_fail (self->vertex_preamble != NULL, NULL);
|
|
g_return_val_if_fail (self->fragment_source != NULL, NULL);
|
|
g_return_val_if_fail (self->vertex_source != NULL, NULL);
|
|
g_return_val_if_fail (self->driver != NULL, NULL);
|
|
|
|
gsk_ngl_command_queue_make_current (self->driver->command_queue);
|
|
|
|
g_snprintf (version, sizeof version, "#version %d\n", self->glsl_version);
|
|
|
|
if (self->debug_shaders)
|
|
debug = "#define GSK_DEBUG 1\n";
|
|
|
|
if (self->legacy)
|
|
legacy = "#define GSK_LEGACY 1\n";
|
|
|
|
if (self->gles)
|
|
gles = "#define GSK_GLES 1\n";
|
|
|
|
if (self->gl3)
|
|
gl3 = "#define GSK_GL3 1\n";
|
|
|
|
vertex_id = glCreateShader (GL_VERTEX_SHADER);
|
|
glShaderSource (vertex_id,
|
|
9,
|
|
(const char *[]) {
|
|
version, debug, legacy, gl3, gles,
|
|
get_shader_string (self->all_preamble),
|
|
get_shader_string (self->vertex_preamble),
|
|
get_shader_string (self->vertex_source),
|
|
get_shader_string (self->vertex_suffix),
|
|
},
|
|
(int[]) {
|
|
strlen (version),
|
|
strlen (debug),
|
|
strlen (legacy),
|
|
strlen (gl3),
|
|
strlen (gles),
|
|
g_bytes_get_size (self->all_preamble),
|
|
g_bytes_get_size (self->vertex_preamble),
|
|
g_bytes_get_size (self->vertex_source),
|
|
g_bytes_get_size (self->vertex_suffix),
|
|
});
|
|
glCompileShader (vertex_id);
|
|
|
|
if (!check_shader_error (vertex_id, error))
|
|
{
|
|
glDeleteShader (vertex_id);
|
|
return NULL;
|
|
}
|
|
|
|
print_shader_info ("Vertex shader", vertex_id, name);
|
|
|
|
fragment_id = glCreateShader (GL_FRAGMENT_SHADER);
|
|
glShaderSource (fragment_id,
|
|
9,
|
|
(const char *[]) {
|
|
version, debug, legacy, gl3, gles,
|
|
get_shader_string (self->all_preamble),
|
|
get_shader_string (self->fragment_preamble),
|
|
get_shader_string (self->fragment_source),
|
|
get_shader_string (self->fragment_suffix),
|
|
},
|
|
(int[]) {
|
|
strlen (version),
|
|
strlen (debug),
|
|
strlen (legacy),
|
|
strlen (gl3),
|
|
strlen (gles),
|
|
g_bytes_get_size (self->all_preamble),
|
|
g_bytes_get_size (self->fragment_preamble),
|
|
g_bytes_get_size (self->fragment_source),
|
|
g_bytes_get_size (self->fragment_suffix),
|
|
});
|
|
glCompileShader (fragment_id);
|
|
|
|
if (!check_shader_error (fragment_id, error))
|
|
{
|
|
glDeleteShader (vertex_id);
|
|
glDeleteShader (fragment_id);
|
|
return NULL;
|
|
}
|
|
|
|
print_shader_info ("Fragment shader", fragment_id, name);
|
|
|
|
program_id = glCreateProgram ();
|
|
glAttachShader (program_id, vertex_id);
|
|
glAttachShader (program_id, fragment_id);
|
|
|
|
for (guint i = 0; i < self->attrib_locations->len; i++)
|
|
{
|
|
const GskNglProgramAttrib *attrib;
|
|
|
|
attrib = &g_array_index (self->attrib_locations, GskNglProgramAttrib, i);
|
|
glBindAttribLocation (program_id, attrib->location, attrib->name);
|
|
}
|
|
|
|
glLinkProgram (program_id);
|
|
|
|
glGetProgramiv (program_id, GL_LINK_STATUS, &status);
|
|
|
|
glDetachShader (program_id, vertex_id);
|
|
glDeleteShader (vertex_id);
|
|
|
|
glDetachShader (program_id, fragment_id);
|
|
glDeleteShader (fragment_id);
|
|
|
|
if (status == GL_FALSE)
|
|
{
|
|
char *buffer = NULL;
|
|
int log_len = 0;
|
|
|
|
glGetProgramiv (program_id, GL_INFO_LOG_LENGTH, &log_len);
|
|
|
|
if (log_len > 0)
|
|
{
|
|
/* log_len includes NULL */
|
|
buffer = g_malloc0 (log_len);
|
|
glGetProgramInfoLog (program_id, log_len, NULL, buffer);
|
|
}
|
|
|
|
g_warning ("Linking failure in shader:\n%s",
|
|
buffer ? buffer : "");
|
|
|
|
g_set_error (error,
|
|
GDK_GL_ERROR,
|
|
GDK_GL_ERROR_LINK_FAILED,
|
|
"Linking failure in shader: %s",
|
|
buffer ? buffer : "");
|
|
|
|
g_free (buffer);
|
|
|
|
glDeleteProgram (program_id);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
return gsk_ngl_program_new (self->driver, name, program_id);
|
|
}
|