Kill support for XML shaders.

Long overdue ...
This commit is contained in:
Themaister 2014-05-25 17:16:33 +02:00
parent 42d501fae1
commit 25e56d8db0
6 changed files with 35 additions and 577 deletions

View File

@ -1354,8 +1354,8 @@ static int menu_common_iterate(void *data, unsigned action)
else
{
unsigned pass = (menu_type - RGUI_SETTINGS_SHADER_0) / 3;
fill_pathname_join(rgui->shader.pass[pass].source.cg,
dir, path, sizeof(rgui->shader.pass[pass].source.cg));
fill_pathname_join(rgui->shader.pass[pass].source.path,
dir, path, sizeof(rgui->shader.pass[pass].source.path));
}
// Pop stack until we hit shader manager again.
@ -1662,8 +1662,8 @@ static void menu_common_shader_manager_init(void *data)
}
else if (strcmp(ext, "glsl") == 0 || strcmp(ext, "cg") == 0)
{
strlcpy(rgui->shader.pass[0].source.cg, g_settings.video.shader_path,
sizeof(rgui->shader.pass[0].source.cg));
strlcpy(rgui->shader.pass[0].source.path, g_settings.video.shader_path,
sizeof(rgui->shader.pass[0].source.path));
rgui->shader.passes = 1;
}
else
@ -1749,9 +1749,9 @@ static void menu_common_shader_manager_get_str(void *data, char *type_str, size_
switch ((type - RGUI_SETTINGS_SHADER_0) % 3)
{
case 0:
if (*shader->pass[pass].source.cg)
if (*shader->pass[pass].source.path)
fill_pathname_base(type_str,
shader->pass[pass].source.cg, type_str_size);
shader->pass[pass].source.path, type_str_size);
else
strlcpy(type_str, "N/A", type_str_size);
break;
@ -1887,7 +1887,7 @@ static unsigned menu_common_shader_manager_get_type(void *data)
for (i = 0; i < shader->passes; i++)
{
enum rarch_shader_type pass_type = gfx_shader_parse_type(shader->pass[i].source.cg,
enum rarch_shader_type pass_type = gfx_shader_parse_type(shader->pass[i].source.path,
RARCH_SHADER_NONE);
switch (pass_type)
@ -1960,7 +1960,7 @@ static int menu_common_shader_manager_setting_toggle(void *data, unsigned settin
case RGUI_ACTION_START:
if (pass)
*pass->source.cg = '\0';
*pass->source.path = '\0';
break;
default:

View File

@ -479,7 +479,7 @@ static bool load_plain(const char *path)
if (path)
{
RARCH_LOG("Loading Cg file: %s\n", path);
strlcpy(cg_shader->pass[0].source.cg, path, sizeof(cg_shader->pass[0].source.cg));
strlcpy(cg_shader->pass[0].source.path, path, sizeof(cg_shader->pass[0].source.path));
if (!load_program(1, path, true))
return false;
}
@ -546,9 +546,9 @@ static bool load_imports(void)
static bool load_shader(unsigned i)
{
RARCH_LOG("Loading Cg shader: \"%s\".\n",
cg_shader->pass[i].source.cg);
cg_shader->pass[i].source.path);
if (!load_program(i + 1, cg_shader->pass[i].source.cg, true))
if (!load_program(i + 1, cg_shader->pass[i].source.path, true))
return false;
return true;

View File

@ -415,11 +415,11 @@ static GLuint compile_program(const char *vertex, const char *fragment, unsigned
static bool load_source_path(struct gfx_shader_pass *pass, const char *path)
{
if (read_file(path, (void**)&pass->source.xml.vertex) <= 0)
if (read_file(path, (void**)&pass->source.string.vertex) <= 0)
return false;
pass->source.xml.fragment = strdup(pass->source.xml.vertex);
return pass->source.xml.fragment && pass->source.xml.vertex;
pass->source.string.fragment = strdup(pass->source.string.vertex);
return pass->source.string.fragment && pass->source.string.vertex;
}
static bool compile_programs(GLuint *gl_prog)
@ -432,15 +432,15 @@ static bool compile_programs(GLuint *gl_prog)
// If we load from GLSLP (CGP),
// load the file here, and pretend
// we were really using XML all along.
if (*pass->source.cg && !load_source_path(pass, pass->source.cg))
if (*pass->source.path && !load_source_path(pass, pass->source.path))
{
RARCH_ERR("Failed to load GLSL shader: %s.\n", pass->source.cg);
RARCH_ERR("Failed to load GLSL shader: %s.\n", pass->source.path);
return false;
}
*pass->source.cg = '\0';
*pass->source.path = '\0';
const char *vertex = pass->source.xml.vertex;
const char *fragment = pass->source.xml.fragment;
const char *vertex = pass->source.string.vertex;
const char *fragment = pass->source.string.fragment;
gl_prog[i] = compile_program(vertex, fragment, i);
@ -588,8 +588,8 @@ static void gl_glsl_free_shader(void)
for (i = 0; i < glsl_shader->passes; i++)
{
free(glsl_shader->pass[i].source.xml.vertex);
free(glsl_shader->pass[i].source.xml.fragment);
free(glsl_shader->pass[i].source.string.vertex);
free(glsl_shader->pass[i].source.string.fragment);
}
free(glsl_shader->script);
@ -667,7 +667,7 @@ static bool gl_glsl_init(void *data, const char *path)
bool ret;
if (strcmp(path_get_extension(path), "glsl") == 0)
{
strlcpy(glsl_shader->pass[0].source.cg, path, sizeof(glsl_shader->pass[0].source.cg));
strlcpy(glsl_shader->pass[0].source.path, path, sizeof(glsl_shader->pass[0].source.path));
glsl_shader->passes = 1;
glsl_shader->modern = true;
ret = true;
@ -685,7 +685,7 @@ static bool gl_glsl_init(void *data, const char *path)
ret = false;
}
else
ret = gfx_shader_read_xml(path, glsl_shader);
ret = false;
if (!ret)
{
@ -697,8 +697,8 @@ static bool gl_glsl_init(void *data, const char *path)
{
RARCH_WARN("[GL]: Stock GLSL shaders will be used.\n");
glsl_shader->passes = 1;
glsl_shader->pass[0].source.xml.vertex = strdup(glsl_core ? stock_vertex_core : stock_vertex_modern);
glsl_shader->pass[0].source.xml.fragment = strdup(glsl_core ? stock_fragment_core : stock_fragment_modern);
glsl_shader->pass[0].source.string.vertex = strdup(glsl_core ? stock_vertex_core : stock_vertex_modern);
glsl_shader->pass[0].source.string.fragment = strdup(glsl_core ? stock_fragment_core : stock_fragment_modern);
glsl_shader->modern = true;
}

View File

@ -20,14 +20,6 @@
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_LIBXML2
#include <libxml/parser.h>
#include <libxml/tree.h>
#else
#define RXML_LIBXML2_COMPAT
#include "../compat/rxml/rxml.h"
#endif
#define print_buf(buf, ...) snprintf(buf, sizeof(buf), __VA_ARGS__)
static const char *wrap_mode_to_str(enum gfx_wrap_type type)
@ -71,7 +63,7 @@ static bool shader_parse_pass(config_file_t *conf, struct gfx_shader_pass *pass,
// Source
char shader_name[64];
print_buf(shader_name, "shader%u", i);
if (!config_get_path(conf, shader_name, pass->source.cg, sizeof(pass->source.cg)))
if (!config_get_path(conf, shader_name, pass->source.path, sizeof(pass->source.path)))
{
RARCH_ERR("Couldn't parse shader source (%s).\n", shader_name);
return false;
@ -410,539 +402,6 @@ bool gfx_shader_read_conf_cgp(config_file_t *conf, struct gfx_shader *shader)
return true;
}
// XML shaders
static bool xml_get_prop(char *buf, size_t size, xmlNodePtr node, const char *prop)
{
if (!size)
return false;
xmlChar *p = xmlGetProp(node, (const xmlChar*)prop);
if (p)
{
bool ret = strlcpy(buf, (const char*)p, size) < size;
xmlFree(p);
return ret;
}
else
{
*buf = '\0';
return false;
}
}
static char *xml_get_content(xmlNodePtr node)
{
xmlChar *content = xmlNodeGetContent(node);
if (!content)
return NULL;
char *ret = strdup((const char*)content);
xmlFree(content);
return ret;
}
static char *xml_replace_if_file(char *content, const char *path, xmlNodePtr node, const char *src_prop)
{
char prop[PATH_MAX];
if (!xml_get_prop(prop, sizeof(prop), node, src_prop))
return content;
free(content);
content = NULL;
char shader_path[PATH_MAX];
fill_pathname_resolve_relative(shader_path, path, prop, sizeof(shader_path));
RARCH_LOG("Loading external source from \"%s\".\n", shader_path);
if (read_file(shader_path, (void**)&content) >= 0)
return content;
else
return NULL;
}
static bool get_xml_attrs(struct gfx_shader_pass *pass, xmlNodePtr ptr)
{
struct gfx_fbo_scale *fbo = &pass->fbo;
pass->frame_count_mod = 0;
pass->filter = RARCH_FILTER_UNSPEC;
fbo->fp_fbo = false;
fbo->scale_x = 1.0;
fbo->scale_y = 1.0;
fbo->type_x = pass->fbo.type_y = RARCH_SCALE_INPUT;
fbo->valid = false;
// Check if shader forces a certain texture filtering.
char attr[64];
if (xml_get_prop(attr, sizeof(attr), ptr, "filter"))
{
if (strcmp(attr, "nearest") == 0)
{
pass->filter = RARCH_FILTER_NEAREST;
RARCH_LOG("XML: Shader forces GL_NEAREST.\n");
}
else if (strcmp(attr, "linear") == 0)
{
pass->filter = RARCH_FILTER_LINEAR;
RARCH_LOG("XML: Shader forces GL_LINEAR.\n");
}
else
{
RARCH_WARN("XML: Invalid property for filter.\n");
return false;
}
}
// Check for scaling attributes *lots of code <_<*
char attr_scale[64], attr_scale_x[64], attr_scale_y[64];
char attr_size[64], attr_size_x[64], attr_size_y[64];
char attr_outscale[64], attr_outscale_x[64], attr_outscale_y[64];
char frame_count_mod[64], fp_fbo[64];
xml_get_prop(attr_scale, sizeof(attr_scale), ptr, "scale");
xml_get_prop(attr_scale_x, sizeof(attr_scale_x), ptr, "scale_x");
xml_get_prop(attr_scale_y, sizeof(attr_scale_y), ptr, "scale_y");
xml_get_prop(attr_size, sizeof(attr_size), ptr, "size");
xml_get_prop(attr_size_x, sizeof(attr_size_x), ptr, "size_x");
xml_get_prop(attr_size_y, sizeof(attr_size_y), ptr, "size_y");
xml_get_prop(attr_outscale, sizeof(attr_outscale), ptr, "outscale");
xml_get_prop(attr_outscale_x, sizeof(attr_outscale_x), ptr, "outscale_x");
xml_get_prop(attr_outscale_y, sizeof(attr_outscale_y), ptr, "outscale_y");
xml_get_prop(frame_count_mod, sizeof(frame_count_mod), ptr, "frame_count_mod");
xml_get_prop(fp_fbo, sizeof(fp_fbo), ptr, "float_framebuffer");
fbo->fp_fbo = strcmp(fp_fbo, "true") == 0;
unsigned x_attr_cnt = 0, y_attr_cnt = 0;
if (*frame_count_mod)
{
pass->frame_count_mod = strtoul(frame_count_mod, NULL, 0);
RARCH_LOG("Got frame count mod attr: %u\n", pass->frame_count_mod);
}
if (*attr_scale)
{
float scale = strtod(attr_scale, NULL);
fbo->scale_x = scale;
fbo->scale_y = scale;
fbo->valid = true;
RARCH_LOG("Got scale attr: %.1f\n", scale);
x_attr_cnt++;
y_attr_cnt++;
}
if (*attr_scale_x)
{
float scale = strtod(attr_scale_x, NULL);
fbo->scale_x = scale;
fbo->valid = true;
RARCH_LOG("Got scale_x attr: %.1f\n", scale);
x_attr_cnt++;
}
if (*attr_scale_y)
{
float scale = strtod(attr_scale_y, NULL);
fbo->scale_y = scale;
fbo->valid = true;
RARCH_LOG("Got scale_y attr: %.1f\n", scale);
y_attr_cnt++;
}
if (*attr_size)
{
fbo->abs_x = fbo->abs_y = strtoul(attr_size, NULL, 0);
fbo->valid = true;
fbo->type_x = fbo->type_y = RARCH_SCALE_ABSOLUTE;
RARCH_LOG("Got size attr: %u\n", fbo->abs_x);
x_attr_cnt++;
y_attr_cnt++;
}
if (*attr_size_x)
{
fbo->abs_x = strtoul(attr_size_x, NULL, 0);
fbo->valid = true;
fbo->type_x = RARCH_SCALE_ABSOLUTE;
RARCH_LOG("Got size_x attr: %u\n", fbo->abs_x);
x_attr_cnt++;
}
if (*attr_size_y)
{
fbo->abs_y = strtoul(attr_size_y, NULL, 0);
fbo->valid = true;
fbo->type_y = RARCH_SCALE_ABSOLUTE;
RARCH_LOG("Got size_y attr: %u\n", fbo->abs_y);
y_attr_cnt++;
}
if (*attr_outscale)
{
float scale = strtod(attr_outscale, NULL);
fbo->scale_x = fbo->scale_y = scale;
fbo->valid = true;
fbo->type_x = fbo->type_y = RARCH_SCALE_VIEWPORT;
RARCH_LOG("Got outscale attr: %.1f\n", scale);
x_attr_cnt++;
y_attr_cnt++;
}
if (*attr_outscale_x)
{
float scale = strtod(attr_outscale_x, NULL);
fbo->scale_x = scale;
fbo->valid = true;
fbo->type_x = RARCH_SCALE_VIEWPORT;
RARCH_LOG("Got outscale_x attr: %.1f\n", scale);
x_attr_cnt++;
}
if (*attr_outscale_y)
{
float scale = strtod(attr_outscale_y, NULL);
fbo->scale_y = scale;
fbo->valid = true;
fbo->type_y = RARCH_SCALE_VIEWPORT;
RARCH_LOG("Got outscale_y attr: %.1f\n", scale);
y_attr_cnt++;
}
if (x_attr_cnt > 1)
return false;
if (y_attr_cnt > 1)
return false;
return true;
}
static bool add_texture_image(struct gfx_shader *shader,
xmlNodePtr ptr)
{
if (shader->luts >= GFX_MAX_TEXTURES)
{
RARCH_WARN("Too many texture images. Ignoring ...\n");
return true;
}
struct gfx_shader_lut *lut = &shader->lut[shader->luts];
xml_get_prop(lut->id, sizeof(lut->id), ptr, "id");
xml_get_prop(lut->path, sizeof(lut->path), ptr, "file");
char filter[64] = {0};
xml_get_prop(filter, sizeof(filter), ptr, "filter");
if (!*lut->id)
{
RARCH_ERR("Could not find ID in texture.\n");
return false;
}
if (!*lut->path)
{
RARCH_ERR("Could not find filename in texture.\n");
return false;
}
if (strcmp(filter, "linear") == 0)
lut->filter = RARCH_FILTER_LINEAR;
else if (strcmp(filter, "nearest") == 0)
lut->filter = RARCH_FILTER_NEAREST;
else if (!*filter)
lut->filter = RARCH_FILTER_UNSPEC;
else
{
RARCH_ERR("Invalid LUT filter type.\n");
return false;
}
shader->luts++;
return true;
}
static bool add_import_value(struct gfx_shader *shader, xmlNodePtr ptr)
{
if (shader->variables >= GFX_MAX_VARIABLES)
{
RARCH_ERR("Too many import variables ...\n");
return false;
}
struct state_tracker_uniform_info *var = &shader->variable[shader->variables];
char semantic[64], wram[64], input[64], bitmask[64], bitequal[64];
xml_get_prop(var->id, sizeof(var->id), ptr, "id");
xml_get_prop(semantic, sizeof(semantic), ptr, "semantic");
xml_get_prop(wram, sizeof(wram), ptr, "wram");
xml_get_prop(input, sizeof(input), ptr, "input_slot");
xml_get_prop(bitmask, sizeof(bitmask), ptr, "mask");
xml_get_prop(bitequal, sizeof(bitequal), ptr, "equal");
if (!*semantic || !*var->id)
{
RARCH_ERR("No semantic or ID for import value.\n");
return false;
}
if (strcmp(semantic, "capture") == 0)
var->type = RARCH_STATE_CAPTURE;
else if (strcmp(semantic, "capture_previous") == 0)
var->type = RARCH_STATE_CAPTURE_PREV;
else if (strcmp(semantic, "transition") == 0)
var->type = RARCH_STATE_TRANSITION;
else if (strcmp(semantic, "transition_count") == 0)
var->type = RARCH_STATE_TRANSITION_COUNT;
else if (strcmp(semantic, "transition_previous") == 0)
var->type = RARCH_STATE_TRANSITION_PREV;
else if (strcmp(semantic, "python") == 0)
var->type = RARCH_STATE_PYTHON;
else
{
RARCH_ERR("Invalid semantic for import value.\n");
return false;
}
if (var->type != RARCH_STATE_PYTHON)
{
if (*input)
{
unsigned slot = strtoul(input, NULL, 0);
switch (slot)
{
case 1:
var->ram_type = RARCH_STATE_INPUT_SLOT1;
break;
case 2:
var->ram_type = RARCH_STATE_INPUT_SLOT2;
break;
default:
RARCH_ERR("Invalid input slot for import.\n");
return false;
}
}
else if (*wram)
{
var->addr = strtoul(wram, NULL, 16);
var->ram_type = RARCH_STATE_WRAM;
}
else
{
RARCH_ERR("No RAM address specificed for import value.\n");
return false;
}
}
if (*bitmask)
var->mask = strtoul(bitmask, NULL, 16);
if (*bitequal)
var->equal = strtoul(bitequal, NULL, 16);
shader->variables++;
return true;
}
static bool get_script(struct gfx_shader *shader, const char *path,
xmlNodePtr ptr)
{
if (shader->script)
{
RARCH_ERR("Script already imported.\n");
return false;
}
xml_get_prop(shader->script_class, sizeof(shader->script_class), ptr, "class");
char language[64];
xml_get_prop(language, sizeof(language), ptr, "language");
if (strcmp(language, "python") != 0)
{
RARCH_ERR("Script language is not Python.\n");
return false;
}
shader->script = xml_get_content(ptr);
if (!shader->script)
return false;
shader->script = xml_replace_if_file(shader->script, path, ptr, "src");
if (!shader->script)
{
RARCH_ERR("Cannot find Python script.\n");
return false;
}
return true;
}
bool gfx_shader_read_xml(const char *path, struct gfx_shader *shader)
{
LIBXML_TEST_VERSION;
xmlParserCtxtPtr ctx = xmlNewParserCtxt();
if (!ctx)
{
RARCH_ERR("Failed to load libxml2 context.\n");
return false;
}
RARCH_LOG("Loading XML shader: %s\n", path);
xmlDocPtr doc = xmlCtxtReadFile(ctx, path, NULL, 0);
xmlNodePtr head = NULL;
xmlNodePtr cur = NULL;
if (!doc)
{
RARCH_ERR("Failed to parse XML file: %s\n", path);
goto error;
}
#ifdef HAVE_LIBXML2
if (ctx->valid == 0)
{
RARCH_ERR("Cannot validate XML shader: %s\n", path);
goto error;
}
#endif
head = xmlDocGetRootElement(doc);
for (cur = head; cur; cur = cur->next)
{
if (cur->type != XML_ELEMENT_NODE)
continue;
if (strcmp((const char*)cur->name, "shader") != 0)
continue;
char attr[64];
xml_get_prop(attr, sizeof(attr), cur, "language");
if (strcmp(attr, "GLSL") == 0)
shader->type = RARCH_SHADER_GLSL;
else
continue;
xml_get_prop(attr, sizeof(attr), cur, "style");
shader->modern = strcmp(attr, "GLES2") == 0;
if (xml_get_prop(shader->prefix, sizeof(shader->prefix), cur, "prefix"))
RARCH_LOG("[GL]: Using uniform and attrib prefix: %s\n", shader->prefix);
if (shader->modern)
RARCH_LOG("[GL]: Shader reports a GLES2 style shader.\n");
else
RARCH_WARN("[GL]: Legacy shaders are deprecated.\n");
break;
}
if (!cur) // We couldn't find any GLSL shader :(
goto error;
// Iterate to check if we find fragment and/or vertex shaders.
for (cur = cur->children; cur && shader->passes < GFX_MAX_SHADERS;
cur = cur->next)
{
if (cur->type != XML_ELEMENT_NODE)
continue;
char *content = xml_get_content(cur);
if (!content)
continue;
struct gfx_shader_pass *pass = &shader->pass[shader->passes];
if (strcmp((const char*)cur->name, "vertex") == 0)
{
if (pass->source.xml.vertex)
{
RARCH_ERR("Cannot have more than one vertex shader in a program.\n");
free(content);
goto error;
}
content = xml_replace_if_file(content, path, cur, "src");
if (!content)
{
RARCH_ERR("Shader source file was provided, but failed to read.\n");
goto error;
}
pass->source.xml.vertex = content;
}
else if (strcmp((const char*)cur->name, "fragment") == 0)
{
if (shader->modern && !pass->source.xml.vertex)
{
RARCH_ERR("Modern GLSL was chosen and vertex shader was not provided. This is an error.\n");
free(content);
goto error;
}
content = xml_replace_if_file(content, path, cur, "src");
if (!content)
{
RARCH_ERR("Shader source file was provided, but failed to read.\n");
goto error;
}
pass->source.xml.fragment = content;
if (!get_xml_attrs(pass, cur))
{
RARCH_ERR("XML shader attributes do not comply with specifications.\n");
goto error;
}
shader->passes++;
}
else if (strcmp((const char*)cur->name, "texture") == 0)
{
free(content);
if (!add_texture_image(shader, cur))
{
RARCH_ERR("Texture image failed to load.\n");
goto error;
}
}
else if (strcmp((const char*)cur->name, "import") == 0)
{
free(content);
if (!add_import_value(shader, cur))
{
RARCH_ERR("Import value is invalid.\n");
goto error;
}
}
else if (strcmp((const char*)cur->name, "script") == 0)
{
free(content);
if (!get_script(shader, path, cur))
{
RARCH_ERR("Script is invalid.\n");
goto error;
}
}
}
if (!shader->passes)
{
RARCH_ERR("Couldn't find vertex shader nor fragment shader in XML file.\n");
goto error;
}
xmlFreeDoc(doc);
xmlFreeParserCtxt(ctx);
return true;
error:
RARCH_ERR("Failed to load XML shader ...\n");
if (doc)
xmlFreeDoc(doc);
xmlFreeParserCtxt(ctx);
return false;
}
// CGP store
static const char *scale_type_to_str(enum gfx_scale_type type)
{
@ -1059,7 +518,7 @@ void gfx_shader_write_conf_cgp(config_file_t *conf, const struct gfx_shader *sha
char key[64];
print_buf(key, "shader%u", i);
config_set_string(conf, key, pass->source.cg);
config_set_string(conf, key, pass->source.path);
if (pass->filter != RARCH_FILTER_UNSPEC)
{
@ -1158,12 +617,12 @@ void gfx_shader_resolve_relative(struct gfx_shader *shader, const char *ref_path
char tmp_path[PATH_MAX];
for (i = 0; i < shader->passes; i++)
{
if (!*shader->pass[i].source.cg)
if (!*shader->pass[i].source.path)
continue;
strlcpy(tmp_path, shader->pass[i].source.cg, sizeof(tmp_path));
fill_pathname_resolve_relative(shader->pass[i].source.cg,
ref_path, tmp_path, sizeof(shader->pass[i].source.cg));
strlcpy(tmp_path, shader->pass[i].source.path, sizeof(tmp_path));
fill_pathname_resolve_relative(shader->pass[i].source.path,
ref_path, tmp_path, sizeof(shader->pass[i].source.path));
}
for (i = 0; i < shader->luts; i++)

View File

@ -69,12 +69,12 @@ struct gfx_shader_pass
{
struct
{
char cg[PATH_MAX];
char path[PATH_MAX];
struct
{
char *vertex; // Dynamically allocated. Must be free'd.
char *fragment; // Dynamically allocated. Must be free'd.
} xml;
} string;
} source;
struct gfx_fbo_scale fbo;
@ -116,7 +116,6 @@ struct gfx_shader
};
bool gfx_shader_read_conf_cgp(config_file_t *conf, struct gfx_shader *shader);
bool gfx_shader_read_xml(const char *path, struct gfx_shader *shader);
void gfx_shader_write_conf_cgp(config_file_t *conf, const struct gfx_shader *shader);
void gfx_shader_resolve_relative(struct gfx_shader *shader, const char *ref_path);

View File

@ -159,8 +159,8 @@
# Exact behavior of this option is implementation specific.
# video_crop_overscan = true
# Path to shader. Shader can be either Cg, CGP (Cg preset) or XML/GLSL format if support is enabled.
# video_shader = "/path/to/shader.{cg,cgp,shader}"
# Path to shader. Shader can be either Cg, CGP (Cg preset) or GLSL, GLSLP (GLSL preset)
# video_shader = "/path/to/shader.{cg,cgp,glsl,glslp}"
# Load video_shader on startup.
# Other shaders can still be loaded later in runtime.