quakeforge/libs/video/renderer/glsl/glsl_shader.c

186 lines
4.3 KiB
C
Raw Normal View History

/*
glsl_shader.c
Shader management based on The OpenGL Shader Wrangler
http://prideout.net/blog/?p=11
Copyright (C) 2013 Bill Currie <bill@taniwha.org>
Author: Bill Currie <bill@taniwha.org>
Date: 2013/02/26
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "QF/alloc.h"
#include "QF/dstring.h"
#include "QF/hash.h"
#include "QF/segtext.h"
#include "QF/sys.h"
#include "QF/va.h"
#include "QF/GLSL/qf_vid.h"
typedef struct glsl_effect_s {
struct glsl_effect_s *next;
const char *name;
segtext_t *text;
} glsl_effect_t;
static hashtab_t *effect_tab;
ALLOC_STATE (glsl_effect_t, effects);
static glsl_effect_t *
new_effect (void)
{
glsl_effect_t *effect;
ALLOC (64, glsl_effect_t, effects, effect);
return effect;
}
static const char *
effect_get_key (const void *e, void *unused)
{
glsl_effect_t *effect = (glsl_effect_t *) e;
return effect->name;
}
static void
effect_free (void *e, void *unused)
{
glsl_effect_t *effect = e;
free ((char *) effect->name);
Segtext_delete (effect->text);
}
void
GLSL_ShaderShutdown (void)
{
Hash_DelTable (effect_tab);
ALLOC_FREE_BLOCKS (effects);
}
int
GLSL_RegisterEffect (const char *name, const char *src)
{
glsl_effect_t *effect;
segtext_t *text;
if (!effect_tab)
effect_tab = Hash_NewTable (61, effect_get_key, effect_free, 0, 0);
if (Hash_Find (effect_tab, name)) {
Sys_Printf ("WARNING: ignoring duplicate '%s' effect\n", name);
return 0;
}
text = Segtext_new (src);
if (!text) {
Sys_Printf ("WARNING: %s: problem parsing effect source\n", name);
return 0;
}
effect = new_effect ();
effect->name = strdup (name);
effect->text = text;
Hash_Add (effect_tab, effect);
return 1;
}
shader_t *
GLSL_BuildShader (const char **effect_keys)
{
const char *dot;
const char **key;
int num_keys = 0;
shader_t *shader;
dstring_t *ekey;
glsl_effect_t *effect;
const segchunk_t *chunk;
for (key = effect_keys; *key; key++)
num_keys++;
shader = malloc (sizeof (shader_t));
shader->num_strings = num_keys;
shader->strings = calloc (2 * num_keys, sizeof (const char *));
shader->src = shader->strings + num_keys;
ekey = dstring_new ();
for (key = effect_keys; *key; key++) {
int num = key - effect_keys;
dot = strchr (*key, '.');
if (!dot) {
Sys_Printf ("Invalid effect key: '%s'\n", *key);
goto error;
}
dstring_copysubstr (ekey, *key, dot - *key);
effect = Hash_Find (effect_tab, ekey->str);
if (!effect) {
Sys_Printf ("Unknown effect: '%s'\n", ekey->str);
goto error;
}
dot++;
chunk = Segtext_FindChunk (effect->text, dot);
if (!chunk) {
Sys_Printf ("Unknown shader key: '%s'\n", dot);
goto error;
}
if (strncmp ("#version ", chunk->text, 9) == 0) {
const char *eol = strchr (chunk->text, '\n');
int vline_len = eol ? eol - chunk->text + 1 : 0;
shader->strings[num] = nva ("%.*s#line %d\n%s", vline_len,
chunk->text,
chunk->start_line + 1,
chunk->text + vline_len);
} else {
shader->strings[num] = nva ("#line %d\n%s", chunk->start_line,
chunk->text);
}
shader->src[num] = strdup (ekey->str);
}
dstring_delete (ekey);
return shader;
error:
// there is guaranteed to be a null in the array if an error occurs.
for (key = shader->strings; *key; key++) {
free ((char *) *key);
free ((char *) shader->src[key - shader->strings]);
}
free (shader->strings);
free (shader);
dstring_delete (ekey);
return 0;
}
void
GLSL_FreeShader (shader_t *shader)
{
int i;
for (i = 0; i < shader->num_strings; i++) {
free ((char *) shader->strings[i]);
free ((char *) shader->src[i]);
}
free (shader->strings);
free (shader);
}