[gtk+/wip/ebassi/gsk-renderer: 54/79] gsk: Add ShaderBuilder
- From: Emmanuele Bassi <ebassi src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/wip/ebassi/gsk-renderer: 54/79] gsk: Add ShaderBuilder
- Date: Wed, 20 Jul 2016 17:05:42 +0000 (UTC)
commit f959d2c6b7485fb9e07b6af8d4150ac7c7caa212
Author: Emmanuele Bassi <ebassi gnome org>
Date: Sun Jul 3 20:04:40 2016 +0100
gsk: Add ShaderBuilder
GskShaderBuilder is an ancillary, private type that deals with the
internals of taking GLSL shaders from resources and building them,
with the additional feature of being able to compose shaders from a
common preamble, as well as adding conditional defines (useful for
enabling debugging code in the shaders themselves).
gsk/Makefile.am | 6 +-
gsk/gskshaderbuilder.c | 359 +++++++++++++++++++++++++++++++++++++++++
gsk/gskshaderbuilderprivate.h | 45 +++++
3 files changed, 408 insertions(+), 2 deletions(-)
---
diff --git a/gsk/Makefile.am b/gsk/Makefile.am
index d3efb7c..7edabe3 100644
--- a/gsk/Makefile.am
+++ b/gsk/Makefile.am
@@ -37,9 +37,10 @@ gsk_private_source_h = \
gskcairorendererprivate.h \
gskdebugprivate.h \
gskglrendererprivate.h \
+ gskprivate.h \
gskrendererprivate.h \
gskrendernodeprivate.h \
- gskprivate.h
+ gskshaderbuilderprivate.h
gsk_private_source_c = \
gskprivate.c
gsk_built_source_h = \
@@ -54,7 +55,8 @@ gsk_source_c = \
gskglrenderer.c \
gskrenderer.c \
gskrendernode.c \
- gskrendernodeiter.c
+ gskrendernodeiter.c \
+ gskshaderbuilder.c
all_sources = \
$(gsk_public_source_h) \
diff --git a/gsk/gskshaderbuilder.c b/gsk/gskshaderbuilder.c
new file mode 100644
index 0000000..16a0e1e
--- /dev/null
+++ b/gsk/gskshaderbuilder.c
@@ -0,0 +1,359 @@
+#include "config.h"
+
+#include "gskshaderbuilderprivate.h"
+
+#include <gdk/gdk.h>
+#include <epoxy/gl.h>
+
+struct _GskShaderBuilder
+{
+ GObject parent_instance;
+
+ char *resource_base_path;
+
+ int version;
+
+ int program_id;
+
+ GPtrArray *defines;
+ GPtrArray *uniforms;
+ GPtrArray *attributes;
+
+ GHashTable *uniform_locations;
+ GHashTable *attribute_locations;
+};
+
+G_DEFINE_TYPE (GskShaderBuilder, gsk_shader_builder, G_TYPE_OBJECT)
+
+static void
+gsk_shader_builder_finalize (GObject *gobject)
+{
+ GskShaderBuilder *self = GSK_SHADER_BUILDER (gobject);
+
+ g_free (self->resource_base_path);
+
+ g_clear_pointer (&self->defines, g_ptr_array_unref);
+ g_clear_pointer (&self->uniforms, g_ptr_array_unref);
+ g_clear_pointer (&self->attributes, g_ptr_array_unref);
+
+ g_clear_pointer (&self->uniform_locations, g_hash_table_unref);
+ g_clear_pointer (&self->attribute_locations, g_hash_table_unref);
+
+ G_OBJECT_CLASS (gsk_shader_builder_parent_class)->finalize (gobject);
+}
+
+static void
+gsk_shader_builder_class_init (GskShaderBuilderClass *klass)
+{
+ G_OBJECT_CLASS (klass)->finalize = gsk_shader_builder_finalize;
+}
+
+static void
+gsk_shader_builder_init (GskShaderBuilder *self)
+{
+ self->defines = g_ptr_array_new_with_free_func (g_free);
+ self->uniforms = g_ptr_array_new_with_free_func (g_free);
+ self->attributes = g_ptr_array_new_with_free_func (g_free);
+
+ self->uniform_locations = g_hash_table_new (g_direct_hash, g_direct_equal);
+ self->attribute_locations = g_hash_table_new (g_direct_hash, g_direct_equal);
+}
+
+GskShaderBuilder *
+gsk_shader_builder_new (void)
+{
+ return g_object_new (GSK_TYPE_SHADER_BUILDER, NULL);
+}
+
+void
+gsk_shader_builder_set_resource_base_path (GskShaderBuilder *builder,
+ const char *base_path)
+{
+ g_return_if_fail (GSK_IS_SHADER_BUILDER (builder));
+
+ g_free (builder->resource_base_path);
+ builder->resource_base_path = g_strdup (base_path);
+}
+
+void
+gsk_shader_builder_set_version (GskShaderBuilder *builder,
+ int version)
+{
+ g_return_if_fail (GSK_IS_SHADER_BUILDER (builder));
+
+ builder->version = version;
+}
+
+void
+gsk_shader_builder_add_define (GskShaderBuilder *builder,
+ const char *define_name,
+ const char *define_value)
+{
+ g_return_if_fail (GSK_IS_SHADER_BUILDER (builder));
+ g_return_if_fail (define_name != NULL && *define_name != '\0');
+ g_return_if_fail (define_value != NULL && *define_name != '\0');
+
+ g_ptr_array_add (builder->defines, g_strdup (define_name));
+ g_ptr_array_add (builder->defines, g_strdup (define_value));
+}
+
+GQuark
+gsk_shader_builder_add_uniform (GskShaderBuilder *builder,
+ const char *uniform_name)
+{
+ g_return_val_if_fail (GSK_IS_SHADER_BUILDER (builder), 0);
+ g_return_val_if_fail (uniform_name != NULL, 0);
+
+ g_ptr_array_add (builder->uniforms, g_strdup (uniform_name));
+
+ return g_quark_from_string (uniform_name);
+}
+
+GQuark
+gsk_shader_builder_add_attribute (GskShaderBuilder *builder,
+ const char *attribute_name)
+{
+ g_return_val_if_fail (GSK_IS_SHADER_BUILDER (builder), 0);
+ g_return_val_if_fail (attribute_name != NULL, 0);
+
+ g_ptr_array_add (builder->attributes, g_strdup (attribute_name));
+
+ return g_quark_from_string (attribute_name);
+}
+
+static gboolean
+lookup_shader_code (GString *code,
+ const char *base_path,
+ const char *shader_file,
+ GError **error)
+{
+ GBytes *source;
+ char *path;
+
+ if (base_path != NULL)
+ path = g_build_filename (base_path, shader_file, NULL);
+ else
+ path = g_strdup (shader_file);
+
+ source = g_resources_lookup_data (path, 0, error);
+ g_free (path);
+
+ if (source == NULL)
+ return FALSE;
+
+ g_string_append (code, g_bytes_get_data (source, NULL));
+
+ g_bytes_unref (source);
+
+ return TRUE;
+}
+
+int
+gsk_shader_builder_compile_shader (GskShaderBuilder *builder,
+ int shader_type,
+ const char *shader_preamble,
+ const char *shader_source,
+ GError **error)
+{
+ GString *code;
+ char *source;
+ int shader_id;
+ int status;
+ int i;
+
+ g_return_val_if_fail (GSK_IS_SHADER_BUILDER (builder), -1);
+ g_return_val_if_fail (shader_source != NULL, -1);
+
+ code = g_string_new (NULL);
+
+ if (builder->version > 0)
+ {
+ g_string_append_printf (code, "#version %d\n", builder->version);
+ g_string_append_c (code, '\n');
+ }
+
+ for (i = 0; i < builder->defines->len; i += 2)
+ {
+ const char *name = g_ptr_array_index (builder->defines, i);
+ const char *value = g_ptr_array_index (builder->defines, i + 1);
+
+ g_string_append (code, "#define");
+ g_string_append_c (code, ' ');
+ g_string_append (code, name);
+ g_string_append_c (code, ' ');
+ g_string_append (code, value);
+ g_string_append_c (code, '\n');
+
+ if (i == builder->defines->len - 2)
+ g_string_append_c (code, '\n');
+ }
+
+ if (!lookup_shader_code (code, builder->resource_base_path, shader_preamble, error))
+ {
+ g_string_free (code, TRUE);
+ return -1;
+ }
+
+ if (!lookup_shader_code (code, builder->resource_base_path, shader_source, error))
+ {
+ g_string_free (code, TRUE);
+ return -1;
+ }
+
+ source = g_string_free (code, FALSE);
+
+ shader_id = glCreateShader (shader_type);
+ glShaderSource (shader_id, 1, (const GLchar **) &source, NULL);
+ glCompileShader (shader_id);
+
+ g_free (source);
+
+ glGetShaderiv (shader_id, GL_COMPILE_STATUS, &status);
+ if (status == GL_FALSE)
+ {
+ int log_len;
+ char *buffer;
+
+ glGetShaderiv (shader_id, GL_INFO_LOG_LENGTH, &log_len);
+
+ buffer = g_malloc0 (log_len + 1);
+ glGetShaderInfoLog (shader_id, log_len, NULL, buffer);
+
+ g_set_error (error, GDK_GL_ERROR, GDK_GL_ERROR_COMPILATION_FAILED,
+ "Compilation failure in %s shader:\n%s",
+ shader_type == GL_VERTEX_SHADER ? "vertex" : "fragment",
+ buffer);
+ g_free (buffer);
+
+ glDeleteShader (shader_id);
+
+ return -1;
+ }
+
+ return shader_id;
+}
+
+static void
+gsk_shader_builder_cache_uniforms (GskShaderBuilder *builder,
+ int program_id)
+{
+ int i;
+
+ for (i = 0; i < builder->uniforms->len; i++)
+ {
+ const char *uniform = g_ptr_array_index (builder->uniforms, i);
+ int loc = glGetUniformLocation (program_id, uniform);
+
+ g_hash_table_insert (builder->uniform_locations,
+ GINT_TO_POINTER (g_quark_from_string (uniform)),
+ GINT_TO_POINTER (loc));
+ }
+}
+
+static void
+gsk_shader_builder_cache_attributes (GskShaderBuilder *builder,
+ int program_id)
+{
+ int i;
+
+ for (i = 0; i < builder->attributes->len; i++)
+ {
+ const char *attribute = g_ptr_array_index (builder->attributes, i);
+ int loc = glGetAttribLocation (program_id, attribute);
+
+ g_hash_table_insert (builder->attribute_locations,
+ GINT_TO_POINTER (g_quark_from_string (attribute)),
+ GINT_TO_POINTER (loc));
+ }
+}
+
+int
+gsk_shader_builder_create_program (GskShaderBuilder *builder,
+ int vertex_id,
+ int fragment_id,
+ GError **error)
+{
+ int program_id;
+ int status;
+
+ g_return_val_if_fail (GSK_IS_SHADER_BUILDER (builder), -1);
+ g_return_val_if_fail (vertex_id > 0, -1);
+ g_return_val_if_fail (fragment_id > 0, -1);
+
+ 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_set_error (error, GDK_GL_ERROR, GDK_GL_ERROR_LINK_FAILED,
+ "Linking failure in shader:\n%s", buffer);
+ g_free (buffer);
+
+ glDeleteProgram (program_id);
+ program_id = -1;
+
+ goto out;
+ }
+
+ gsk_shader_builder_cache_uniforms (builder, program_id);
+ gsk_shader_builder_cache_attributes (builder, program_id);
+
+ builder->program_id = program_id;
+
+out:
+ glDetachShader (program_id, vertex_id);
+ glDetachShader (program_id, fragment_id);
+
+ return program_id;
+}
+
+int
+gsk_shader_builder_get_uniform_location (GskShaderBuilder *builder,
+ GQuark uniform_quark)
+{
+ gpointer loc_p = NULL;
+
+ g_return_val_if_fail (GSK_IS_SHADER_BUILDER (builder), -1);
+
+ if (builder->program_id < 0)
+ return -1;
+
+ if (builder->uniforms->len == 0)
+ return -1;
+
+ if (g_hash_table_lookup_extended (builder->uniform_locations, GINT_TO_POINTER (uniform_quark), NULL,
&loc_p))
+ return GPOINTER_TO_INT (loc_p);
+
+ return -1;
+}
+
+int
+gsk_shader_builder_get_attribute_location (GskShaderBuilder *builder,
+ GQuark attribute_quark)
+{
+ gpointer loc_p = NULL;
+
+ g_return_val_if_fail (GSK_IS_SHADER_BUILDER (builder), -1);
+
+ if (builder->program_id < 0)
+ return -1;
+
+ if (builder->attributes->len == 0)
+ return -1;
+
+ if (g_hash_table_lookup_extended (builder->attribute_locations, GINT_TO_POINTER (attribute_quark), NULL,
&loc_p))
+ return GPOINTER_TO_INT (loc_p);
+
+ return -1;
+}
diff --git a/gsk/gskshaderbuilderprivate.h b/gsk/gskshaderbuilderprivate.h
new file mode 100644
index 0000000..f5f7ff8
--- /dev/null
+++ b/gsk/gskshaderbuilderprivate.h
@@ -0,0 +1,45 @@
+#ifndef __GSK_SHADER_BUILDER_PRIVATE_H__
+#define __GSK_SHADER_BUILDER_PRIVATE_H__
+
+#include <gdk/gdk.h>
+#include <graphene.h>
+
+G_BEGIN_DECLS
+
+#define GSK_TYPE_SHADER_BUILDER (gsk_shader_builder_get_type ())
+
+G_DECLARE_FINAL_TYPE (GskShaderBuilder, gsk_shader_builder, GSK, SHADER_BUILDER, GObject)
+
+GskShaderBuilder * gsk_shader_builder_new (void);
+
+void gsk_shader_builder_set_version (GskShaderBuilder *builder,
+ int version);
+void gsk_shader_builder_set_resource_base_path (GskShaderBuilder *builder,
+ const char *base_path);
+
+GQuark gsk_shader_builder_add_uniform (GskShaderBuilder *builder,
+ const char *uniform_name);
+GQuark gsk_shader_builder_add_attribute (GskShaderBuilder *builder,
+ const char *attribute_name);
+void gsk_shader_builder_add_define (GskShaderBuilder *builder,
+ const char *define_name,
+ const char *define_value);
+
+int gsk_shader_builder_compile_shader (GskShaderBuilder *builder,
+ int shader_type,
+ const char *shader_preamble,
+ const char *shader_source,
+ GError **error);
+int gsk_shader_builder_create_program (GskShaderBuilder *builder,
+ int vertex_id,
+ int fragment_id,
+ GError **error);
+
+int gsk_shader_builder_get_uniform_location (GskShaderBuilder *builder,
+ GQuark uniform_quark);
+int gsk_shader_builder_get_attribute_location (GskShaderBuilder *builder,
+ GQuark attribute_quark);
+
+G_END_DECLS
+
+#endif /* __GSK_SHADER_BUILDER_PRIVATE_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]