diff --git a/gfx/shader_glsl.c b/gfx/shader_glsl.c index 588fefee73..2c6b7d5af9 100644 --- a/gfx/shader_glsl.c +++ b/gfx/shader_glsl.c @@ -55,14 +55,6 @@ #include "gfx_context.h" #include -#ifdef HAVE_LIBXML2 -#include -#include -#else -#define RXML_LIBXML2_COMPAT -#include "../compat/rxml/rxml.h" -#endif - #include "gl_common.h" #include "image.h" @@ -126,61 +118,24 @@ static PFNGLVERTEXATTRIBPOINTERPROC pglVertexAttribPointer; #define BORDER_FUNC GL_CLAMP_TO_BORDER #endif -#define MAX_VARIABLES 256 -#define MAX_TEXTURES 8 #define PREV_TEXTURES 7 -enum filter_type -{ - RARCH_GL_NOFORCE, - RARCH_GL_LINEAR, - RARCH_GL_NEAREST -}; - -static char glsl_prefix[64]; +static struct gfx_shader *glsl_shader; static bool glsl_enable; static bool glsl_modern; -static GLuint gl_program[RARCH_GLSL_MAX_SHADERS]; -static enum filter_type gl_filter_type[RARCH_GLSL_MAX_SHADERS]; -static struct gfx_fbo_scale gl_scale[RARCH_GLSL_MAX_SHADERS]; -static unsigned gl_num_programs; +static GLuint gl_program[GFX_MAX_SHADERS]; static unsigned active_index; -static GLuint gl_teximage[MAX_TEXTURES]; -static unsigned gl_teximage_cnt; -static char gl_teximage_uniforms[MAX_TEXTURES][64]; +static GLuint gl_teximage[GFX_MAX_TEXTURES]; static state_tracker_t *gl_state_tracker; -static struct state_tracker_uniform_info gl_tracker_info[MAX_VARIABLES]; -static unsigned gl_tracker_info_cnt; -static char gl_tracker_script_class[64]; - -static char *gl_script_program; static GLint gl_attribs[PREV_TEXTURES + 1 + 4 + RARCH_GLSL_MAX_SHADERS]; static unsigned gl_attrib_index; static gfx_ctx_proc_t (*glsl_get_proc_address)(const char*); -struct shader_program -{ - char *vertex; - char *fragment; - enum filter_type filter; - - bool fp_fbo; - float scale_x; - float scale_y; - unsigned abs_x; - unsigned abs_y; - enum gfx_scale_type type_x; - enum gfx_scale_type type_y; - unsigned frame_count_mod; - - bool valid_scale; -}; - struct shader_uniforms_frame { int texture; @@ -205,7 +160,7 @@ struct shader_uniforms unsigned frame_count_mod; int frame_direction; - int lut_texture[MAX_TEXTURES]; + int lut_texture[GFX_MAX_TEXTURES]; struct shader_uniforms_frame orig; struct shader_uniforms_frame pass[RARCH_GLSL_MAX_SHADERS]; @@ -214,6 +169,11 @@ struct shader_uniforms static struct shader_uniforms gl_uniforms[RARCH_GLSL_MAX_SHADERS]; +static const char *glsl_prefixes[] = { + "", + "ruby", +}; + static const char *stock_vertex_legacy = "varying vec4 color;\n" "void main() {\n" @@ -253,224 +213,18 @@ static const char *stock_fragment_modern = " gl_FragColor = color * texture2D(Texture, tex_coord);\n" "}"; -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[64]; - 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, (const char*)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 shader_program *prog, xmlNodePtr ptr) -{ - prog->frame_count_mod = 0; - prog->fp_fbo = false; - prog->scale_x = 1.0; - prog->scale_y = 1.0; - prog->type_x = prog->type_y = RARCH_SCALE_INPUT; - prog->valid_scale = 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) - { - prog->filter = RARCH_GL_NEAREST; - RARCH_LOG("XML: Shader forces GL_NEAREST.\n"); - } - else if (strcmp(attr, "linear") == 0) - { - prog->filter = RARCH_GL_LINEAR; - RARCH_LOG("XML: Shader forces GL_LINEAR.\n"); - } - else - RARCH_WARN("XML: Invalid property for filter.\n"); - } - else - prog->filter = RARCH_GL_NOFORCE; - - // 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"); - - prog->fp_fbo = strcmp(fp_fbo, "true") == 0; - - unsigned x_attr_cnt = 0, y_attr_cnt = 0; - - if (*frame_count_mod) - { - prog->frame_count_mod = strtoul(frame_count_mod, NULL, 0); - RARCH_LOG("Got frame count mod attr: %u\n", prog->frame_count_mod); - } - - if (*attr_scale) - { - float scale = strtod(attr_scale, NULL); - prog->scale_x = scale; - prog->scale_y = scale; - prog->valid_scale = true; - prog->type_x = prog->type_y = RARCH_SCALE_INPUT; - 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); - prog->scale_x = scale; - prog->valid_scale = true; - prog->type_x = RARCH_SCALE_INPUT; - RARCH_LOG("Got scale_x attr: %.1f\n", scale); - x_attr_cnt++; - } - - if (*attr_scale_y) - { - float scale = strtod(attr_scale_y, NULL); - prog->scale_y = scale; - prog->valid_scale = true; - prog->type_y = RARCH_SCALE_INPUT; - RARCH_LOG("Got scale_y attr: %.1f\n", scale); - y_attr_cnt++; - } - - if (*attr_size) - { - prog->abs_x = prog->abs_y = strtoul(attr_size, NULL, 0); - prog->valid_scale = true; - prog->type_x = prog->type_y = RARCH_SCALE_ABSOLUTE; - RARCH_LOG("Got size attr: %u\n", prog->abs_x); - x_attr_cnt++; - y_attr_cnt++; - } - - if (*attr_size_x) - { - prog->abs_x = strtoul(attr_size_x, NULL, 0); - prog->valid_scale = true; - prog->type_x = RARCH_SCALE_ABSOLUTE; - RARCH_LOG("Got size_x attr: %u\n", prog->abs_x); - x_attr_cnt++; - } - - if (*attr_size_y) - { - prog->abs_y = strtoul(attr_size_y, NULL, 0); - prog->valid_scale = true; - prog->type_y = RARCH_SCALE_ABSOLUTE; - RARCH_LOG("Got size_y attr: %u\n", prog->abs_y); - y_attr_cnt++; - } - - if (*attr_outscale) - { - float scale = strtod(attr_outscale, NULL); - prog->scale_x = scale; - prog->scale_y = scale; - prog->valid_scale = true; - prog->type_x = prog->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); - prog->scale_x = scale; - prog->valid_scale = true; - prog->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); - prog->scale_y = scale; - prog->valid_scale = true; - prog->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 const char *glsl_prefixes[] = { - glsl_prefix, - "", - "ruby", -}; static GLint get_uniform(GLuint prog, const char *base) { + char buf[64]; + + snprintf(buf, sizeof(buf), "%s%s", glsl_shader->prefix, base); + GLint loc = pglGetUniformLocation(prog, buf); + if (loc >= 0) + return loc; + for (unsigned i = 0; i < ARRAY_SIZE(glsl_prefixes); i++) { - char buf[64]; snprintf(buf, sizeof(buf), "%s%s", glsl_prefixes[i], base); GLint loc = pglGetUniformLocation(prog, buf); if (loc >= 0) @@ -482,9 +236,14 @@ static GLint get_uniform(GLuint prog, const char *base) static GLint get_attrib(GLuint prog, const char *base) { + char buf[64]; + snprintf(buf, sizeof(buf), "%s%s", glsl_shader->prefix, base); + GLint loc = pglGetUniformLocation(prog, buf); + if (loc >= 0) + return loc; + for (unsigned i = 0; i < ARRAY_SIZE(glsl_prefixes); i++) { - char buf[64]; snprintf(buf, sizeof(buf), "%s%s", glsl_prefixes[i], base); GLint loc = pglGetAttribLocation(prog, buf); if (loc >= 0) @@ -494,391 +253,52 @@ static GLint get_attrib(GLuint prog, const char *base) return -1; } -static bool get_texture_image(const char *shader_path, xmlNodePtr ptr) +static bool load_luts(const char *shader_path) { - if (gl_teximage_cnt >= MAX_TEXTURES) - { - RARCH_WARN("Too many texture images. Ignoring ...\n"); + if (!glsl_shader->luts) return true; - } - bool linear = true; - char filename[PATH_MAX]; - char filter[64]; - char id[64]; - xml_get_prop(filename, sizeof(filename), ptr, "file"); - xml_get_prop(filter, sizeof(filter), ptr, "filter"); - xml_get_prop(id, sizeof(id), ptr, "id"); - struct texture_image img; + glGenTextures(1, gl_teximage); - if (!*id) + for (unsigned i = 0; i < glsl_shader->luts; i++) { - RARCH_ERR("Could not find ID in texture.\n"); - return false; - } + char tex_path[PATH_MAX]; + fill_pathname_resolve_relative(tex_path, + shader_path, glsl_shader->lut[i].path, sizeof(tex_path)); - if (!*filename) - { - RARCH_ERR("Could not find filename in texture.\n"); - return false; - } + RARCH_LOG("Loading texture image from: \"%s\" ...\n", tex_path); - if (strcmp(filter, "nearest") == 0) - linear = false; - - char tex_path[PATH_MAX]; - fill_pathname_resolve_relative(tex_path, shader_path, (const char*)filename, sizeof(tex_path)); - - RARCH_LOG("Loading texture image from: \"%s\" ...\n", tex_path); - - if (!texture_image_load(tex_path, &img)) - { - RARCH_ERR("Failed to load texture image from: \"%s\"\n", tex_path); - return false; - } - - strlcpy(gl_teximage_uniforms[gl_teximage_cnt], (const char*)id, sizeof(gl_teximage_uniforms[0])); - - glGenTextures(1, &gl_teximage[gl_teximage_cnt]); - - pglActiveTexture(GL_TEXTURE0 + gl_teximage_cnt + 1); - glBindTexture(GL_TEXTURE_2D, gl_teximage[gl_teximage_cnt]); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, BORDER_FUNC); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, BORDER_FUNC); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, linear ? GL_LINEAR : GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, linear ? GL_LINEAR : GL_NEAREST); - - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); - glTexImage2D(GL_TEXTURE_2D, - 0, driver.gfx_use_rgba ? GL_RGBA : RARCH_GL_INTERNAL_FORMAT32, - img.width, img.height, 0, driver.gfx_use_rgba ? GL_RGBA : RARCH_GL_TEXTURE_TYPE32, - RARCH_GL_FORMAT32, img.pixels); - - pglActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, 0); - free(img.pixels); - - gl_teximage_cnt++; - - return true; -} - -#ifdef HAVE_PYTHON -static bool get_script(const char *path, xmlNodePtr ptr) -{ - if (gl_script_program) - { - RARCH_ERR("Script already imported.\n"); - return false; - } - - char script_class[64]; - xml_get_prop(script_class, sizeof(script_class), ptr, "class"); - if (*script_class) - strlcpy(gl_tracker_script_class, script_class, sizeof(gl_tracker_script_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; - } - - char *script = xml_get_content(ptr); - if (!script) - return false; - - gl_script_program = xml_replace_if_file(script, path, ptr, "src"); - if (!gl_script_program) - { - RARCH_ERR("Cannot find Python script.\n"); - return false; - } - - return true; -} -#endif - -static bool get_import_value(xmlNodePtr ptr) -{ - if (gl_tracker_info_cnt >= MAX_VARIABLES) - { - RARCH_ERR("Too many import variables ...\n"); - return false; - } - - char id[64], semantic[64], wram[64], input[64], bitmask[64], bitequal[64]; - xml_get_prop(id, sizeof(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"); - - unsigned memtype; - enum state_tracker_type tracker_type; - enum state_ram_type ram_type = RARCH_STATE_NONE; - uint32_t addr = 0; - unsigned mask_value = 0; - unsigned mask_equal = 0; - - if (!*semantic || !*id) - { - RARCH_ERR("No semantic or ID for import value.\n"); - return false; - } - - if (strcmp(semantic, "capture") == 0) - tracker_type = RARCH_STATE_CAPTURE; - else if (strcmp(semantic, "capture_previous") == 0) - tracker_type = RARCH_STATE_CAPTURE_PREV; - else if (strcmp(semantic, "transition") == 0) - tracker_type = RARCH_STATE_TRANSITION; - else if (strcmp(semantic, "transition_count") == 0) - tracker_type = RARCH_STATE_TRANSITION_COUNT; - else if (strcmp(semantic, "transition_previous") == 0) - tracker_type = RARCH_STATE_TRANSITION_PREV; -#ifdef HAVE_PYTHON - else if (strcmp(semantic, "python") == 0) - tracker_type = RARCH_STATE_PYTHON; -#endif - else - { - RARCH_ERR("Invalid semantic for import value.\n"); - return false; - } - -#ifdef HAVE_PYTHON - if (tracker_type != RARCH_STATE_PYTHON) -#endif - { - if (*input) + struct texture_image img = {0}; + if (!texture_image_load(tex_path, &img)) { - unsigned slot = strtoul(input, NULL, 0); - switch (slot) - { - case 1: - ram_type = RARCH_STATE_INPUT_SLOT1; - break; - case 2: - ram_type = RARCH_STATE_INPUT_SLOT2; - break; - - default: - RARCH_ERR("Invalid input slot for import.\n"); - return false; - } - } - else if (*wram) - { - addr = strtoul(wram, NULL, 16); - ram_type = RARCH_STATE_WRAM; - } - else - { - RARCH_ERR("No RAM address specificed for import value.\n"); + RARCH_ERR("Failed to load texture image from: \"%s\"\n", tex_path); return false; } + + glBindTexture(GL_TEXTURE_2D, gl_teximage[i]); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, BORDER_FUNC); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, BORDER_FUNC); + + GLenum filter = glsl_shader->lut[i].filter == RARCH_FILTER_NEAREST ? + GL_NEAREST : GL_LINEAR; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + glTexImage2D(GL_TEXTURE_2D, + 0, driver.gfx_use_rgba ? GL_RGBA : RARCH_GL_INTERNAL_FORMAT32, + img.width, img.height, 0, + driver.gfx_use_rgba ? GL_RGBA : RARCH_GL_TEXTURE_TYPE32, + RARCH_GL_FORMAT32, img.pixels); + + glBindTexture(GL_TEXTURE_2D, 0); + free(img.pixels); } - switch (ram_type) - { - case RARCH_STATE_WRAM: - memtype = RETRO_MEMORY_SYSTEM_RAM; - break; - - default: - memtype = -1u; - } - - if ((memtype != -1u) && (addr >= pretro_get_memory_size(memtype))) - { - RARCH_ERR("Address out of bounds.\n"); - return false; - } - - if (*bitmask) - mask_value = strtoul(bitmask, NULL, 16); - if (*bitequal) - mask_equal = strtoul(bitequal, NULL, 16); - - strlcpy(gl_tracker_info[gl_tracker_info_cnt].id, id, sizeof(gl_tracker_info[0].id)); - gl_tracker_info[gl_tracker_info_cnt].addr = addr; - gl_tracker_info[gl_tracker_info_cnt].type = tracker_type; - gl_tracker_info[gl_tracker_info_cnt].ram_type = ram_type; - gl_tracker_info[gl_tracker_info_cnt].mask = mask_value; - gl_tracker_info[gl_tracker_info_cnt].equal = mask_equal; - gl_tracker_info_cnt++; - return true; } -static unsigned get_xml_shaders(const char *path, struct shader_program *prog, size_t size) -{ - 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; - unsigned num = 0; - - 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) - continue; - - xml_get_prop(attr, sizeof(attr), cur, "style"); - glsl_modern = strcmp(attr, "GLES2") == 0; - - if (xml_get_prop(glsl_prefix, sizeof(glsl_prefix), cur, "prefix")) - RARCH_LOG("[GL]: Using uniform and attrib prefix: %s\n", glsl_prefix); - - if (glsl_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; - - memset(prog, 0, sizeof(struct shader_program) * size); - - // Iterate to check if we find fragment and/or vertex shaders. - for (cur = cur->children; cur && num < size; cur = cur->next) - { - if (cur->type != XML_ELEMENT_NODE) - continue; - - char *content = xml_get_content(cur); - if (!content) - continue; - - if (strcmp((const char*)cur->name, "vertex") == 0) - { - if (prog[num].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; - } - - prog[num].vertex = content; - } - else if (strcmp((const char*)cur->name, "fragment") == 0) - { - if (glsl_modern && !prog[num].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; - } - - prog[num].fragment = content; - if (!get_xml_attrs(&prog[num], cur)) - { - RARCH_ERR("XML shader attributes do not comply with specifications.\n"); - goto error; - } - num++; - } - else if (strcmp((const char*)cur->name, "texture") == 0) - { - free(content); - if (!get_texture_image(path, cur)) - { - RARCH_ERR("Texture image failed to load.\n"); - goto error; - } - } - else if (strcmp((const char*)cur->name, "import") == 0) - { - free(content); - if (!get_import_value(cur)) - { - RARCH_ERR("Import value is invalid.\n"); - goto error; - } - } -#ifdef HAVE_PYTHON - else if (strcmp((const char*)cur->name, "script") == 0) - { - free(content); - if (!get_script(path, cur)) - { - RARCH_ERR("Script is invalid.\n"); - goto error; - } - } -#endif - } - - if (num == 0) - { - RARCH_ERR("Couldn't find vertex shader nor fragment shader in XML file.\n"); - goto error; - } - - xmlFreeDoc(doc); - xmlFreeParserCtxt(ctx); - return num; - -error: - RARCH_ERR("Failed to load XML shader ...\n"); - if (doc) - xmlFreeDoc(doc); - xmlFreeParserCtxt(ctx); - return 0; -} - static void print_shader_log(GLuint obj) { GLint info_len = 0; @@ -952,75 +372,72 @@ static bool link_program(GLuint prog) return false; } -static bool compile_programs(GLuint *gl_prog, struct shader_program *progs, size_t num) +static GLuint compile_program(const char *vertex, const char *fragment, unsigned i) { - bool ret = true; + GLuint prog = pglCreateProgram(); + if (!prog) + return 0; - for (unsigned i = 0; i < num; i++) + if (vertex) { - gl_prog[i] = pglCreateProgram(); + RARCH_LOG("Found GLSL vertex shader.\n"); + GLuint shader = pglCreateShader(GL_VERTEX_SHADER); + if (!compile_shader(shader, vertex)) + { + RARCH_ERR("Failed to compile vertex shader #%u\n", i); + return false; + } - if (gl_prog[i] == 0) + pglAttachShader(prog, shader); + } + + if (fragment) + { + RARCH_LOG("Found GLSL fragment shader.\n"); + GLuint shader = pglCreateShader(GL_FRAGMENT_SHADER); + if (!compile_shader(shader, fragment)) + { + RARCH_ERR("Failed to compile fragment shader #%u\n", i); + return false; + } + + pglAttachShader(prog, shader); + } + + if (vertex || fragment) + { + RARCH_LOG("Linking GLSL program.\n"); + if (!link_program(prog)) + { + RARCH_ERR("Failed to link program #%u.\n", i); + return 0; + } + + pglUseProgram(prog); + GLint location = get_uniform(prog, "Texture"); + pglUniform1i(location, 0); + pglUseProgram(0); + } + + return prog; +} + +static bool compile_programs(GLuint *gl_prog) +{ + for (unsigned i = 0; i < glsl_shader->passes; i++) + { + const char *vertex = glsl_shader->pass[i].source.xml.vertex; + const char *fragment = glsl_shader->pass[i].source.xml.fragment; + gl_prog[i] = compile_program(vertex, fragment, i); + + if (!gl_prog[i]) { RARCH_ERR("Failed to create GL program #%u.\n", i); - ret = false; - goto end; - } - - if (progs[i].vertex) - { - RARCH_LOG("Found GLSL vertex shader.\n"); - GLuint shader = pglCreateShader(GL_VERTEX_SHADER); - if (!compile_shader(shader, progs[i].vertex)) - { - RARCH_ERR("Failed to compile vertex shader #%u\n", i); - ret = false; - goto end; - } - - pglAttachShader(gl_prog[i], shader); - } - - if (progs[i].fragment) - { - RARCH_LOG("Found GLSL fragment shader.\n"); - GLuint shader = pglCreateShader(GL_FRAGMENT_SHADER); - if (!compile_shader(shader, progs[i].fragment)) - { - RARCH_ERR("Failed to compile fragment shader #%u\n", i); - ret = false; - goto end; - } - - pglAttachShader(gl_prog[i], shader); - } - - if (progs[i].vertex || progs[i].fragment) - { - RARCH_LOG("Linking GLSL program.\n"); - if (!link_program(gl_prog[i])) - { - RARCH_ERR("Failed to link program #%u\n", i); - ret = false; - goto end; - } - - GLint location = get_uniform(gl_prog[i], "Texture"); - pglUniform1i(location, 0); - pglUseProgram(0); + return false; } } -end: - for (unsigned i = 0; i < num; i++) - { - free(progs[i].vertex); - free(progs[i].fragment); - progs[i].vertex = NULL; - progs[i].fragment = NULL; - } - - return ret; + return true; } static void gl_glsl_reset_attrib(void) @@ -1065,13 +482,13 @@ static void find_uniforms(GLuint prog, struct shader_uniforms *uni) uni->frame_count = get_uniform(prog, "FrameCount"); uni->frame_direction = get_uniform(prog, "FrameDirection"); - for (unsigned i = 0; i < gl_teximage_cnt; i++) - uni->lut_texture[i] = pglGetUniformLocation(prog, gl_teximage_uniforms[i]); + for (unsigned i = 0; i < glsl_shader->luts; i++) + uni->lut_texture[i] = pglGetUniformLocation(prog, glsl_shader->lut[i].id); find_uniforms_frame(prog, &uni->orig, "Orig"); char frame_base[64]; - for (unsigned i = 0; i < RARCH_GLSL_MAX_SHADERS; i++) + for (unsigned i = 0; i < GFX_MAX_SHADERS; i++) { snprintf(frame_base, sizeof(frame_base), "Pass%u", i + 1); find_uniforms_frame(prog, &uni->pass[i], frame_base); @@ -1102,42 +519,6 @@ static void gl_glsl_delete_shader(GLuint prog) pglDeleteProgram(prog); } -static bool gl_glsl_load_shader(unsigned index, const char *path) -{ - pglUseProgram(0); - - if (gl_program[index] != gl_program[0]) - { - gl_glsl_delete_shader(gl_program[index]); - gl_program[index] = 0; - } - - if (path) - { - struct shader_program prog = {0}; - unsigned progs = get_xml_shaders(path, &prog, 1); - if (progs != 1) - return false; - - if (!compile_programs(&gl_program[index], &prog, 1)) - { - RARCH_ERR("Failed to compile shader: %s.\n", path); - return false; - } - - find_uniforms(gl_program[index], &gl_uniforms[index]); - gl_uniforms[index].frame_count_mod = prog.frame_count_mod; - } - else - { - gl_program[index] = gl_program[0]; - gl_uniforms[index] = gl_uniforms[0]; - } - - pglUseProgram(gl_program[active_index]); - return true; -} - // Platforms with broken get_proc_address. // Assume functions are available without proc_address. #undef LOAD_GL_SYM @@ -1146,6 +527,22 @@ static bool gl_glsl_load_shader(unsigned index, const char *path) memcpy(&(pgl##SYM), &sym, sizeof(sym)); \ } +static void gl_glsl_free_shader(void) +{ + if (!glsl_shader) + return; + + for (unsigned 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->script); + free(glsl_shader); + glsl_shader = NULL; +} + bool gl_glsl_init(const char *path) { #if !defined(HAVE_OPENGLES2) && !defined(HAVE_OPENGL_MODERN) && !defined(__APPLE__) @@ -1193,83 +590,76 @@ bool gl_glsl_init(const char *path) } #endif - unsigned num_progs = 0; - struct shader_program progs[RARCH_GLSL_MAX_SHADERS] = {{0}}; - if (path) - { - num_progs = get_xml_shaders(path, progs, RARCH_GLSL_MAX_SHADERS - 1); + glsl_shader = (struct gfx_shader*)calloc(1, sizeof(*glsl_shader)); + if (!glsl_shader) + return false; - if (num_progs == 0) - { - RARCH_ERR("Couldn't find any valid shaders in XML file.\n"); - return false; - } + if (path && !gfx_shader_read_xml(path, glsl_shader)) + { + RARCH_ERR("[GL]: Failed to parse XML shader.\n"); + return false; } else { RARCH_WARN("[GL]: Stock GLSL shaders will be used.\n"); - num_progs = 1; - progs[0].vertex = strdup(stock_vertex_modern); - progs[0].fragment = strdup(stock_fragment_modern); - glsl_modern = true; + glsl_shader->passes = 1; + glsl_shader->pass[0].source.xml.vertex = strdup(stock_vertex_modern); + glsl_shader->pass[0].source.xml.fragment = strdup(stock_fragment_modern); + glsl_shader->modern = true; } #ifdef HAVE_OPENGLES2 - if (!glsl_modern) + if (!glsl_shader->modern) { RARCH_ERR("[GL]: GLES context is used, but shader is not modern. Cannot use it.\n"); return false; } #endif - struct shader_program stock_prog = {0}; - stock_prog.vertex = strdup(glsl_modern ? stock_vertex_modern : stock_vertex_legacy); - stock_prog.fragment = strdup(glsl_modern ? stock_fragment_modern : stock_fragment_legacy); + const char *stock_vertex = glsl_shader->modern ? + stock_vertex_modern : stock_vertex_legacy; + const char *stock_fragment = glsl_shader->modern ? + stock_fragment_modern : stock_fragment_legacy; - if (!compile_programs(&gl_program[0], &stock_prog, 1)) + if (!(gl_program[0] = compile_program(stock_vertex, stock_fragment, 0))) { RARCH_ERR("GLSL stock programs failed to compile.\n"); + gl_glsl_free_shader(); return false; } - for (unsigned i = 0; i < num_progs; i++) + if (!compile_programs(&gl_program[1])) { - gl_filter_type[i + 1] = progs[i].filter; - gl_scale[i + 1].fp_fbo = progs[i].fp_fbo; - gl_scale[i + 1].type_x = progs[i].type_x; - gl_scale[i + 1].type_y = progs[i].type_y; - gl_scale[i + 1].scale_x = progs[i].scale_x; - gl_scale[i + 1].scale_y = progs[i].scale_y; - gl_scale[i + 1].abs_x = progs[i].abs_x; - gl_scale[i + 1].abs_y = progs[i].abs_y; - gl_scale[i + 1].valid = progs[i].valid_scale; + gl_glsl_free_shader(); + return false; } - if (!compile_programs(&gl_program[1], progs, num_progs)) + if (!load_luts(path)) + { + RARCH_ERR("[GL]: Failed to load LUTs.\n"); + gl_glsl_free_shader(); return false; + } - for (unsigned i = 0; i <= num_progs; i++) + for (unsigned i = 0; i <= glsl_shader->passes; i++) find_uniforms(gl_program[i], &gl_uniforms[i]); - for (unsigned i = 1; i <= num_progs; i++) - gl_uniforms[i].frame_count_mod = progs[i - 1].frame_count_mod; - #ifdef GLSL_DEBUG if (!gl_check_error()) RARCH_WARN("Detected GL error in GLSL.\n"); #endif - if (gl_tracker_info_cnt > 0) + if (glsl_shader->variables) { struct state_tracker_info info = {0}; info.wram = (uint8_t*)pretro_get_memory_data(RETRO_MEMORY_SYSTEM_RAM); - info.info = gl_tracker_info; - info.info_elem = gl_tracker_info_cnt; + info.info = glsl_shader->variable; + info.info_elem = glsl_shader->variables; #ifdef HAVE_PYTHON - info.script = gl_script_program; - info.script_class = *gl_tracker_script_class ? gl_tracker_script_class : NULL; - info.script_is_file = false; + info.script = glsl_shader->script; + info.script_class = *glsl_shader->script_class ? + glsl_shader->script_class : NULL; #endif gl_state_tracker = state_tracker_init(&info); @@ -1277,10 +667,9 @@ bool gl_glsl_init(const char *path) RARCH_WARN("Failed to init state tracker.\n"); } - glsl_enable = true; - gl_num_programs = num_progs; - gl_program[gl_num_programs + 1] = gl_program[0]; - gl_uniforms[gl_num_programs + 1] = gl_uniforms[0]; + glsl_enable = true; + gl_program[glsl_shader->passes + 1] = gl_program[0]; + gl_uniforms[glsl_shader->passes + 1] = gl_uniforms[0]; gl_glsl_reset_attrib(); @@ -1292,7 +681,7 @@ void gl_glsl_deinit(void) if (glsl_enable) { pglUseProgram(0); - for (unsigned i = 0; i <= gl_num_programs; i++) + for (unsigned i = 0; i <= glsl_shader->passes; i++) { if (gl_program[i] == 0 || (i && gl_program[i] == gl_program[0])) continue; @@ -1300,27 +689,19 @@ void gl_glsl_deinit(void) gl_glsl_delete_shader(gl_program[i]); } - glDeleteTextures(gl_teximage_cnt, gl_teximage); - gl_teximage_cnt = 0; - memset(gl_teximage_uniforms, 0, sizeof(gl_teximage_uniforms)); + if (glsl_shader) + glDeleteTextures(glsl_shader->luts, gl_teximage); } memset(gl_program, 0, sizeof(gl_program)); glsl_enable = false; active_index = 0; - gl_tracker_info_cnt = 0; - memset(gl_tracker_info, 0, sizeof(gl_tracker_info)); - memset(gl_tracker_script_class, 0, sizeof(gl_tracker_script_class)); - - free(gl_script_program); - gl_script_program = NULL; + gl_glsl_free_shader(); if (gl_state_tracker) - { state_tracker_free(gl_state_tracker); - gl_state_tracker = NULL; - } + gl_state_tracker = NULL; gl_glsl_reset_attrib(); } @@ -1334,7 +715,7 @@ void gl_glsl_set_params(unsigned width, unsigned height, const struct gl_tex_info *fbo_info, unsigned fbo_info_cnt) { // We enforce a certain layout for our various texture types in the texunits. - // - Regular frame (rubyTexture) (always bound). + // - Regular frame (Texture) (always bound). // - LUT textures (always bound). // - Original texture (always bound if meaningful). // - FBO textures (always bound if available). @@ -1358,24 +739,29 @@ void gl_glsl_set_params(unsigned width, unsigned height, if (uni->texture_size >= 0) pglUniform2fv(uni->texture_size, 1, texture_size); - if (uni->frame_count >= 0) + if (uni->frame_count >= 0 && active_index) { - unsigned count = frame_count; - if (uni->frame_count_mod) - count %= uni->frame_count_mod; - pglUniform1i(uni->frame_count, count); + unsigned modulo = glsl_shader->pass[active_index - 1].frame_count_mod; + if (modulo) + frame_count %= modulo; + pglUniform1i(uni->frame_count, frame_count); } if (uni->frame_direction >= 0) pglUniform1i(uni->frame_direction, g_extern.frame_is_reverse ? -1 : 1); - for (unsigned i = 0; i < gl_teximage_cnt; i++) + for (unsigned i = 0; i < glsl_shader->luts; i++) { if (uni->lut_texture[i] >= 0) + { + // Have to rebind as HW render could override this. + pglActiveTexture(GL_TEXTURE0 + i + 1); + glBindTexture(GL_TEXTURE_2D, gl_teximage[i]); pglUniform1i(uni->lut_texture[i], i + 1); + } } - unsigned texunit = gl_teximage_cnt + 1; + unsigned texunit = glsl_shader->luts + 1; // Set original texture unless we're in first pass (pointless). if (active_index > 1) @@ -1446,7 +832,7 @@ void gl_glsl_set_params(unsigned width, unsigned height, // Unbind any lurking FBO passes. // Rendering to a texture that is bound to a texture unit // sounds very shaky ... ;) - for (unsigned i = 0; i < gl_num_programs; i++) + for (unsigned i = 0; i < glsl_shader->passes; i++) { pglActiveTexture(GL_TEXTURE0 + base_tex + i); glBindTexture(GL_TEXTURE_2D, 0); @@ -1485,11 +871,11 @@ void gl_glsl_set_params(unsigned width, unsigned height, if (gl_state_tracker) { - static struct state_tracker_uniform info[MAX_VARIABLES]; + static struct state_tracker_uniform info[GFX_MAX_VARIABLES]; static unsigned cnt = 0; if (active_index == 1) - cnt = state_get_uniform(gl_state_tracker, info, MAX_VARIABLES, frame_count); + cnt = state_get_uniform(gl_state_tracker, info, GFX_MAX_VARIABLES, frame_count); for (unsigned i = 0; i < cnt; i++) { @@ -1565,36 +951,26 @@ void gl_glsl_use(unsigned index) unsigned gl_glsl_num(void) { - return gl_num_programs; + return glsl_shader->passes; } bool gl_glsl_filter_type(unsigned index, bool *smooth) { - if (!glsl_enable) - return false; - - switch (gl_filter_type[index]) + if (glsl_enable && index) { - case RARCH_GL_NOFORCE: - return false; - - case RARCH_GL_NEAREST: - *smooth = false; - return true; - - case RARCH_GL_LINEAR: - *smooth = true; - return true; - - default: + if (glsl_shader->pass[index - 1].filter == RARCH_FILTER_UNSPEC) return false; + *smooth = glsl_shader->pass[index - 1].filter == RARCH_FILTER_LINEAR; + return true; } + else + return false; } void gl_glsl_shader_scale(unsigned index, struct gfx_fbo_scale *scale) { - if (glsl_enable) - *scale = gl_scale[index]; + if (glsl_enable && index) + *scale = glsl_shader->pass[index - 1].fbo; else scale->valid = false; } @@ -1615,7 +991,7 @@ const gl_shader_backend_t gl_glsl_backend = { gl_glsl_set_coords, gl_glsl_set_mvp, - gl_glsl_load_shader, + NULL, RARCH_SHADER_GLSL, }; diff --git a/gfx/shader_parse.c b/gfx/shader_parse.c index 66ad25c937..48afcd0222 100644 --- a/gfx/shader_parse.c +++ b/gfx/shader_parse.c @@ -20,8 +20,17 @@ #include #include +#ifdef HAVE_LIBXML2 +#include +#include +#else +#define RXML_LIBXML2_COMPAT +#include "../compat/rxml/rxml.h" +#endif + #define print_buf(buf, ...) snprintf(buf, sizeof(buf), __VA_ARGS__) +// CGP static bool shader_parse_pass(config_file_t *conf, struct gfx_shader_pass *pass, unsigned i) { // Source @@ -304,6 +313,8 @@ bool gfx_shader_read_conf_cgp(config_file_t *conf, struct gfx_shader *shader) { memset(shader, 0, sizeof(*shader)); + shader->type = RARCH_SHADER_CG; + unsigned shaders = 0; if (!config_get_uint(conf, "shaders", &shaders)) { @@ -333,6 +344,544 @@ 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"); + + uint32_t addr = 0; + unsigned mask_value = 0; + unsigned mask_equal = 0; + + 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) { switch (type) diff --git a/gfx/shader_parse.h b/gfx/shader_parse.h index ef7b2c2665..905fb277ae 100644 --- a/gfx/shader_parse.h +++ b/gfx/shader_parse.h @@ -57,10 +57,14 @@ struct gfx_fbo_scale struct gfx_shader_pass { - union + struct { char cg[PATH_MAX]; - // Can allow for more types later. + struct + { + char *vertex; // Dynamically allocated. Must be free'd. + char *fragment; // Dynamically allocated. Must be free'd. + } xml; } source; struct gfx_fbo_scale fbo; @@ -79,6 +83,11 @@ struct gfx_shader_lut // Avoid lots of allocation for convenience. struct gfx_shader { + enum rarch_shader_type type; + + bool modern; // Only used for XML shaders. + char prefix[64]; + unsigned passes; struct gfx_shader_pass pass[GFX_MAX_SHADERS]; @@ -88,10 +97,12 @@ struct gfx_shader unsigned variables; struct state_tracker_uniform_info variable[GFX_MAX_VARIABLES]; char script_path[PATH_MAX]; + char *script; // Dynamically allocated. Must be free'd. Only used by XML. char script_class[512]; }; 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); enum rarch_shader_type gfx_shader_parse_type(const char *path, enum rarch_shader_type fallback);