forked from AuroraMiddleware/gtk
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).
This commit is contained in:
parent
3d21c4afba
commit
2ded2ad6b7
@ -38,9 +38,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 = \
|
||||
@ -55,7 +56,8 @@ gsk_source_c = \
|
||||
gskglrenderer.c \
|
||||
gskrenderer.c \
|
||||
gskrendernode.c \
|
||||
gskrendernodeiter.c
|
||||
gskrendernodeiter.c \
|
||||
gskshaderbuilder.c
|
||||
|
||||
all_sources = \
|
||||
$(gsk_public_source_h) \
|
||||
|
359
gsk/gskshaderbuilder.c
Normal file
359
gsk/gskshaderbuilder.c
Normal file
@ -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;
|
||||
}
|
45
gsk/gskshaderbuilderprivate.h
Normal file
45
gsk/gskshaderbuilderprivate.h
Normal file
@ -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__ */
|
Loading…
Reference in New Issue
Block a user