diff --git a/skeleton/BASE/Shaders/glsl/lcd1x.glsl b/skeleton/BASE/Shaders/glsl/lcd1x.glsl new file mode 100644 index 000000000..d0df05851 --- /dev/null +++ b/skeleton/BASE/Shaders/glsl/lcd1x.glsl @@ -0,0 +1,124 @@ +/* + lcd1x shader + + A slightly tweaked version of lcd3x, original code written by Gigaherz + and released into the public domain: + + > Omits LCD 'colour seperation' effect + + > Has 'properly' aligned scanlines + + Edited by jdgleaver + + 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. +*/ + +#pragma parameter BRIGHTEN_SCANLINES "Brighten Scanlines" 16.0 1.0 32.0 0.5 +#pragma parameter BRIGHTEN_LCD "Brighten LCD" 4.0 1.0 12.0 0.1 + +#if defined(VERTEX) + +#if __VERSION__ >= 130 +#define COMPAT_VARYING out +#define COMPAT_ATTRIBUTE in +#define COMPAT_TEXTURE texture +#else +#define COMPAT_VARYING varying +#define COMPAT_ATTRIBUTE attribute +#define COMPAT_TEXTURE texture2D +#endif + +#ifdef GL_ES +#define COMPAT_PRECISION highp +#else +#define COMPAT_PRECISION +#endif + +COMPAT_ATTRIBUTE vec4 VertexCoord; +COMPAT_ATTRIBUTE vec4 COLOR; +COMPAT_ATTRIBUTE vec4 TexCoord; +COMPAT_VARYING vec4 TEX0; + +uniform mat4 MVPMatrix; +uniform COMPAT_PRECISION int FrameDirection; +uniform COMPAT_PRECISION int FrameCount; +uniform COMPAT_PRECISION vec2 OutputSize; +uniform COMPAT_PRECISION vec2 TextureSize; +uniform COMPAT_PRECISION vec2 InputSize; + +/* + VERTEX_SHADER +*/ +void main() +{ + TEX0 = TexCoord * 1.0001; + gl_Position = MVPMatrix * VertexCoord; +} + +#elif defined(FRAGMENT) + +#if __VERSION__ >= 130 +#define COMPAT_VARYING in +#define COMPAT_TEXTURE texture +out vec4 FragColor; +#else +#define COMPAT_VARYING varying +#define FragColor gl_FragColor +#define COMPAT_TEXTURE texture2D +#endif + +#ifdef GL_ES +precision highp float; +precision highp int; +#define COMPAT_PRECISION highp +#else +#define COMPAT_PRECISION +#endif + +uniform COMPAT_PRECISION int FrameDirection; +uniform COMPAT_PRECISION int FrameCount; +uniform COMPAT_PRECISION vec2 OrigInputSize; +uniform COMPAT_PRECISION vec2 TextureSize; +uniform COMPAT_PRECISION vec2 InputSize; +uniform COMPAT_PRECISION vec2 OutputSize; +uniform sampler2D Texture; +COMPAT_VARYING vec4 TEX0; + +#ifdef PARAMETER_UNIFORM +// All parameter floats need to have COMPAT_PRECISION in front of them +uniform COMPAT_PRECISION float BRIGHTEN_SCANLINES; +uniform COMPAT_PRECISION float BRIGHTEN_LCD; +#else +#define BRIGHTEN_SCANLINES 16.0 +#define BRIGHTEN_LCD 4.0 +#endif + +// Magic Numbers +#define PI 3.141592654 + +/* + FRAGMENT SHADER +*/ +void main() +{ + // Generate LCD grid effect + // > Note the 0.25 pixel offset -> required to ensure that + // scanlines occur *between* pixels + COMPAT_PRECISION vec2 angle = + 2.0 * PI * ((TEX0.xy * OrigInputSize * TextureSize / InputSize) - 0.25); + + COMPAT_PRECISION float yfactor = (BRIGHTEN_SCANLINES + sin(angle.y)) / (BRIGHTEN_SCANLINES + 1.0); + COMPAT_PRECISION float xfactor = (BRIGHTEN_LCD + sin(angle.x)) / (BRIGHTEN_LCD + 1.0); + + // Get colour sample + COMPAT_PRECISION vec3 colour = COMPAT_TEXTURE(Texture, TEX0.xy).rgb; + + // Apply LCD grid effect + colour.rgb = yfactor * xfactor * colour.rgb; + + FragColor = vec4(colour.rgb, 1.0); +} +#endif diff --git a/workspace/all/common/api.h b/workspace/all/common/api.h index a1ceffca7..9db138a51 100644 --- a/workspace/all/common/api.h +++ b/workspace/all/common/api.h @@ -212,6 +212,19 @@ enum { MODE_MAIN, MODE_MENU, }; +#include "opengl.h" +#define MAX_PARAM_NAME 128 +#define MAX_PARAM_LABEL 128 +typedef struct { + char name[MAX_PARAM_NAME]; + char label[MAX_PARAM_LABEL]; + float def; + float min; + float max; + float step; + float value; + GLint uniformLocation; +} ShaderParam; SDL_Surface* GFX_init(int mode); #define GFX_resize PLAT_resizeVideo // (int w, int h, int pitch); @@ -568,6 +581,7 @@ void PLAT_setShader2(const char* filename); void PLAT_setShader3(const char* filename); void PLAT_updateShader(int i, const char *filename, int *scale, int *filter, int *scaletype, int *inputtype); void PLAT_initShaders(); +ShaderParam* PLAT_getShaderPragmas(int i); int PLAT_supportsOverscan(void); SDL_Surface* PLAT_initOverlay(void); diff --git a/workspace/all/minarch/minarch.c b/workspace/all/minarch/minarch.c index d13fb10f6..a5f061528 100644 --- a/workspace/all/minarch/minarch.c +++ b/workspace/all/minarch/minarch.c @@ -1199,6 +1199,7 @@ enum { SYNC_SRC_CORE }; enum { + SH_EXTRASETTINGS, SH_SHADERS_PRESET, SH_NROFSHADERS, SH_SHADER1, @@ -1377,6 +1378,7 @@ static struct Config { OptionList frontend; OptionList core; OptionList shaders; + OptionList shaderpragmas; ButtonMapping* controls; ButtonMapping* shortcuts; int loaded; @@ -1536,8 +1538,18 @@ static struct Config { }, }, .shaders = { // (OptionList) - .count = 17, + .count = 18, .options = (Option[]){ + [SH_EXTRASETTINGS] = { + .key = "minarch_shaders_settings", + .name = "Extra settings", + .desc = "Load a premade shaders config", // will call getScreenScalingDesc() + .default_value = 1, + .value = 1, + .count = 0, + .values = NULL, + .labels = NULL, + }, [SH_SHADERS_PRESET] = { .key = "minarch_shaders_preset", .name = "Preset", @@ -1714,6 +1726,12 @@ static struct Config { {NULL} }, }, + .shaderpragmas = { + .count = 0, + .options = (Option[]) { + {NULL}, + } + }, .controls = default_button_mapping, .shortcuts = (ButtonMapping[]){ [SHORTCUT_SAVE_STATE] = {"Save State", -1, BTN_ID_NONE, 0}, @@ -1750,6 +1768,8 @@ static int Config_getValue(char* cfg, const char* key, char* out_value, int* loc + + static void setOverclock(int i) { overclock = i; switch (i) { @@ -2068,7 +2088,6 @@ static void Config_readOptionsString(char* cfg) { int device = strtol(gamepad_values[gamepad_type], NULL, 0); core.set_controller_port_device(0, device); } - for (int i=0; config.core.options[i].key; i++) { Option* option = &config.core.options[i]; // LOG_info("%s\n",option->key); @@ -2078,9 +2097,15 @@ static void Config_readOptionsString(char* cfg) { for (int i=0; config.shaders.options[i].key; i++) { Option* option = &config.shaders.options[i]; if (!Config_getValue(cfg, option->key, value, &option->lock)) continue; - LOG_info("set value toch %s %s\n",option->key,value); OptionList_setOptionValue(&config.shaders, option->key, value); } + if(config.shaderpragmas.count > 0) { + for (int i=0; config.shaderpragmas.options[i].key; i++) { + Option* option = &config.shaderpragmas.options[i]; + if (!Config_getValue(cfg, option->key, value, &option->lock)) continue; + OptionList_setOptionValue(&config.shaderpragmas, option->key, value); + } + } } static void Config_readControlsString(char* cfg) { if (!cfg) return; @@ -2262,6 +2287,14 @@ static void Config_write(int override) { fprintf(file, "%s = %s\n", option->key, option->values[option->value]); } } + for (int i=0; config.shaderpragmas.options[i].key; i++) { + Option* option = &config.shaderpragmas.options[i]; + int count = 0; + while ( option->values && option->values[count]) count++; + if (option->value >= 0 && option->value < count) { + fprintf(file, "%s = %s\n", option->key, option->values[option->value]); + } + } if (has_custom_controllers) fprintf(file, "%s = %i\n", "minarch_gamepad_type", gamepad_type); @@ -2337,12 +2370,18 @@ static void Config_restore(void) { } void readShadersPreset(int i) { + LOG_info("Doe 44\n"); char shaderspath[MAX_PATH] = {0}; sprintf(shaderspath, SHADERS_FOLDER "/%s", config.shaders.options[SH_SHADERS_PRESET].values[i]); LOG_info("read shaders preset %s\n",shaderspath); - if (exists(shaderspath)) config.shaders_preset = allocFile(shaderspath); + if (exists(shaderspath)) { + config.shaders_preset = allocFile(shaderspath); + Config_readOptionsString(config.shaders_preset); + } else config.shaders_preset = NULL; - Config_readOptionsString(config.shaders_preset); + + + } static void Config_syncShaders(char* key, int value) { @@ -2449,7 +2488,38 @@ static void Config_syncShaders(char* key, int value) { } //////// - +void loadShaderSettings() { + int menucount = 0; + config.shaderpragmas.options = calloc(config.shaders.options[SH_NROFSHADERS].value*32+1, sizeof(Option)); + for (int i=0; i < config.shaders.options[SH_NROFSHADERS].value; i++) { + ShaderParam *params = PLAT_getShaderPragmas(i); + for (int j = 0; j < 32; j++) { + if(params[j].def) { + config.shaderpragmas.options[menucount].key = params[j].name; + config.shaderpragmas.options[menucount].name = params[j].name; + config.shaderpragmas.options[menucount].desc = params[j].name; + config.shaderpragmas.options[menucount].default_value = params[j].def; + config.shaderpragmas.options[menucount].value = params[j].value; + + int steps = (int)((params[j].max - params[j].min) / params[j].step) + 1; + config.shaderpragmas.options[menucount].values = malloc(sizeof(char *) * (steps + 1)); + config.shaderpragmas.options[menucount].labels = malloc(sizeof(char *) * (steps + 1)); + for (int s = 0; s < steps; s++) { + float val = params[j].min + s * params[j].step; + char *str = malloc(16); + snprintf(str, 16, "%.2f", val); + config.shaderpragmas.options[menucount].values[s] = str; + config.shaderpragmas.options[menucount].labels[s] = str; + } + config.shaderpragmas.options[menucount].count = steps; + config.shaderpragmas.options[menucount].values[steps] = NULL; + config.shaderpragmas.options[menucount].labels[steps] = NULL; + menucount++; + } + } + } + config.shaderpragmas.count = menucount; +} void initShaders() { for (int i=0; config.shaders.options[i].key; i++) { if(i!=SH_SHADERS_PRESET) { @@ -2457,6 +2527,24 @@ void initShaders() { Config_syncShaders(option->key, option->value); } } + + + loadShaderSettings(); + Config_readOptions(); + // set shader settings after re-reading conigs + for (int y=0; y < config.shaders.options[SH_NROFSHADERS].value; y++) { + ShaderParam *params = PLAT_getShaderPragmas(y); + if (params == NULL) { + break; + } + for (int i=0; i < config.shaderpragmas.count; i++) { + for (int j = 0; j < 32; j++) { + if (exactMatch(params[j].name, config.shaderpragmas.options[i].key)) { + params[j].value = strtof(config.shaderpragmas.options[i].values[config.shaderpragmas.options[i].value], NULL); + } + } + } + } shadersreload = 0; } @@ -5304,6 +5392,57 @@ static int OptionCheats_openMenu(MenuList* list, int i) { +static int OptionPragmas_optionChanged(MenuList* list, int i) { + MenuItem* item = &list->items[i]; + for (int i=0; i < config.shaders.options[SH_NROFSHADERS].value; i++) { + ShaderParam *params = PLAT_getShaderPragmas(i); + for (int j = 0; j < 32; j++) { + if (exactMatch(params[j].name, item->key)) { + params[j].value = strtof(item->values[item->value], NULL); + } + } + } + for (int i = 0; i < config.shaderpragmas.count; i++) { + MenuItem* item = &list->items[i]; + config.shaderpragmas.options[i].value = item->value ; + + } + return MENU_CALLBACK_NOP; +} + +static MenuList PragmasOptions_menu = { + .type = MENU_FIXED, + .on_confirm = NULL, + .on_change = OptionPragmas_optionChanged, + .items = NULL +}; +static int OptionPragmas_openMenu(MenuList* list, int i) { + LOG_info("OptionPragmas oppenen\n"); + + PragmasOptions_menu.items = calloc(config.shaderpragmas.count + 1, sizeof(MenuItem)); + + for (int i = 0; i < config.shaderpragmas.count; i++) { + + MenuItem* item = &PragmasOptions_menu.items[i]; + Option* configitem = &config.shaderpragmas.options[i]; + item->id = i; + item->name = configitem->name; + item->desc = configitem->desc; + item->value = configitem->value; + item->key = configitem->key; + item->values = configitem->values; + + } + + + if (PragmasOptions_menu.items[0].name) { + Menu_options(&PragmasOptions_menu); + } else { + Menu_message("No extra settings found", (char*[]){"B", "BACK", NULL}); + } + + return MENU_CALLBACK_NOP; +} static int OptionShaders_optionChanged(MenuList* list, int i) { MenuItem* item = &list->items[i]; Config_syncShaders(item->key, item->value); @@ -5313,6 +5452,7 @@ static int OptionShaders_optionChanged(MenuList* list, int i) { item->value = config.shaders.options[i].value; } + initShaders(); return MENU_CALLBACK_NOP; } @@ -5322,6 +5462,7 @@ static MenuList ShaderOptions_menu = { .on_change = OptionShaders_optionChanged, .items = NULL }; + static int OptionShaders_openMenu(MenuList* list, int i) { LOG_info("OptionShaders_openMenu\n"); @@ -5329,34 +5470,37 @@ static int OptionShaders_openMenu(MenuList* list, int i) { int filecount; char** filelist = list_files_in_folder(SHADERS_FOLDER "/glsl", &filecount,NULL); - // Check if folder read failed or no files found - if (!filelist || filecount == 0) { - Menu_message("No shaders available\n/Shaders folder or shader files not found", (char*[]){"B", "BACK", NULL}); - return MENU_CALLBACK_NOP; - } - - // NULL-terminate filelist just in case - filelist = realloc(filelist, sizeof(char*) * (filecount + 1)); - filelist[filecount] = NULL; - ShaderOptions_menu.items = calloc(config.shaders.count + 1, sizeof(MenuItem)); - for (int i = 0; i < config.shaders.count; i++) { - MenuItem* item = &ShaderOptions_menu.items[i]; - item->id = i; - item->name = config.shaders.options[i].name; - item->desc = config.shaders.options[i].desc; - item->value = config.shaders.options[i].value; - item->key = config.shaders.options[i].key; + // Check if folder read failed or no files found + if (!filelist || filecount == 0) { + Menu_message("No shaders available\n/Shaders folder or shader files not found", (char*[]){"B", "BACK", NULL}); + return MENU_CALLBACK_NOP; + } - if (strcmp(config.shaders.options[i].key, "minarch_shader1") == 0 || - strcmp(config.shaders.options[i].key, "minarch_shader2") == 0 || - strcmp(config.shaders.options[i].key, "minarch_shader3") == 0) { - item->values = filelist; - config.shaders.options[i].values = filelist; - } else { - item->values = config.shaders.options[i].values; - } + // NULL-terminate filelist just in case + filelist = realloc(filelist, sizeof(char*) * (filecount + 1)); + filelist[filecount] = NULL; + + ShaderOptions_menu.items = calloc(config.shaders.count + 1, sizeof(MenuItem)); + for (int i = 0; i < config.shaders.count; i++) { + MenuItem* item = &ShaderOptions_menu.items[i]; + Option* configitem = &config.shaders.options[i]; + item->id = i; + item->name = configitem->name; + item->desc = configitem->desc; + item->value = configitem->value; + item->key = configitem->key; + if(i == SH_EXTRASETTINGS) item->on_confirm = OptionPragmas_openMenu; + + if (strcmp(config.shaders.options[i].key, "minarch_shader1") == 0 || + strcmp(config.shaders.options[i].key, "minarch_shader2") == 0 || + strcmp(config.shaders.options[i].key, "minarch_shader3") == 0) { + item->values = filelist; + config.shaders.options[i].values = filelist; + } else { + item->values = config.shaders.options[i].values; } + } if (ShaderOptions_menu.items[0].name) { @@ -6624,7 +6768,7 @@ int main(int argc , char* argv[]) { Input_init(NULL); Config_readOptions(); // but others load and report options later (eg. nes) Config_readControls(); // restore controls (after the core has reported its defaults) - Config_free(); + SND_init(core.sample_rate, core.fps); InitSettings(); // after we initialize audio Menu_init(); @@ -6657,6 +6801,8 @@ int main(int argc , char* argv[]) { // then initialize custom shaders from settings initShaders(); + // release config when all is loaded + Config_free(); LOG_info("total startup time %ims\n\n",SDL_GetTicks()); while (!quit) { GFX_startFrame(); diff --git a/workspace/macos/platform/platform.c b/workspace/macos/platform/platform.c index a934daa60..3259efeda 100644 --- a/workspace/macos/platform/platform.c +++ b/workspace/macos/platform/platform.c @@ -45,8 +45,12 @@ typedef struct Shader { GLint u_OutputSize; GLint u_TextureSize; GLint u_InputSize; + GLint OrigInputSize; GLint texLocation; GLint texelSizeLocation; + ShaderParam *pragmas; // Dynamic array of parsed pragma parameters + int num_pragmas; // Count of valid pragma parameters + } Shader; GLuint g_shader_default = 0; @@ -398,6 +402,46 @@ static uint32_t SDL_transparentBlack = 0; static char* overlay_path = NULL; + +#define MAX_SHADERLINE_LENGTH 512 +int extractPragmaParameters(const char *shaderSource, ShaderParam *params, int maxParams) { + const char *pragmaPrefix = "#pragma parameter"; + char line[MAX_SHADERLINE_LENGTH]; + int paramCount = 0; + + const char *currentPos = shaderSource; + + while (*currentPos && paramCount < maxParams) { + int i = 0; + + // Read a line + while (*currentPos && *currentPos != '\n' && i < MAX_SHADERLINE_LENGTH - 1) { + line[i++] = *currentPos++; + } + line[i] = '\0'; + if (*currentPos == '\n') currentPos++; + + // Check if it's a #pragma parameter line + if (strncmp(line, pragmaPrefix, strlen(pragmaPrefix)) == 0) { + const char *start = line + strlen(pragmaPrefix); + while (*start == ' ') start++; + + ShaderParam *p = ¶ms[paramCount]; + + // Try to parse + if (sscanf(start, "%127s \"%127[^\"]\" %f %f %f %f", + p->name, p->label, &p->def, &p->min, &p->max, &p->step) == 6) { + paramCount++; + } else { + fprintf(stderr, "Failed to parse line:\n%s\n", line); + } + } + } + + return paramCount; // number of parameters found +} + + GLuint link_program(GLuint vertex_shader, GLuint fragment_shader, const char* cache_key) { char cache_path[512]; snprintf(cache_path, sizeof(cache_path), "/mnt/SDCARD/.shadercache/%s.bin", cache_key); @@ -514,7 +558,8 @@ GLuint load_shader_from_file(GLenum type, const char* filename, const char* path "#else\n" "precision mediump float;\n" "#endif\n" - "#endif\n"; + "#endif\n" + "#define PARAMETER_UNIFORM\n"; } else { fprintf(stderr, "Unsupported shader type\n"); free(source); @@ -798,6 +843,21 @@ char* PLAT_findFileInDir(const char *directory, const char *filename) { return NULL; } + +#define MAX_SHADER_PRAGMAS 32 +void loadShaderPragmas(Shader *shader, const char *shaderSource) { + shader->pragmas = calloc(MAX_SHADER_PRAGMAS, sizeof(ShaderParam)); + if (!shader->pragmas) { + fprintf(stderr, "Out of memory allocating pragmas for %s\n", shader->filename); + return; + } + shader->num_pragmas = extractPragmaParameters(shaderSource, shader->pragmas, MAX_SHADER_PRAGMAS); +} + +ShaderParam* PLAT_getShaderPragmas(int i) { + return shaders[i]->pragmas; +} + void PLAT_updateShader(int i, const char *filename, int *scale, int *filter, int *scaletype, int *srctype) { if (i < 0 || i >= nrofshaders) { @@ -808,8 +868,14 @@ void PLAT_updateShader(int i, const char *filename, int *scale, int *filter, int if (filename != NULL) { SDL_GL_MakeCurrent(vid.window, vid.gl_context); LOG_info("loading shader \n"); - GLuint vertex_shader1 = load_shader_from_file(GL_VERTEX_SHADER, filename,SHADERS_FOLDER "/glsl"); - GLuint fragment_shader1 = load_shader_from_file(GL_FRAGMENT_SHADER, filename,SHADERS_FOLDER "/glsl"); + + char filepath[512]; + snprintf(filepath, sizeof(filepath), SHADERS_FOLDER "/glsl/%s",filename); + const char *shaderSource = load_shader_source(filepath); + loadShaderPragmas(shader,shaderSource); + GLuint vertex_shader1 = load_shader_from_file(GL_VERTEX_SHADER, filename,SHADERS_FOLDER "/glsl"); + + // Link the shader program if (shader->shader_p != 0) { @@ -823,8 +889,19 @@ void PLAT_updateShader(int i, const char *filename, int *scale, int *filter, int shader->u_OutputSize = glGetUniformLocation( shader->shader_p, "OutputSize"); shader->u_TextureSize = glGetUniformLocation( shader->shader_p, "TextureSize"); shader->u_InputSize = glGetUniformLocation( shader->shader_p, "InputSize"); + shader->OrigInputSize = glGetUniformLocation( shader->shader_p, "OrigInputSize"); shader->texLocation = glGetUniformLocation(shader->shader_p, "Texture"); shader->texelSizeLocation = glGetUniformLocation(shader->shader_p, "texelSize"); + for (int i = 0; i < shader->num_pragmas; ++i) { + shader->pragmas[i].uniformLocation = glGetUniformLocation(shader->shader_p, shader->pragmas[i].name); + shader->pragmas[i].value = shader->pragmas[i].min; + printf("Param: %s = %f (min: %f, max: %f, step: %f)\n", + shader->pragmas[i].name, + shader->pragmas[i].def, + shader->pragmas[i].min, + shader->pragmas[i].max, + shader->pragmas[i].step); + } if (shader->shader_p == 0) { LOG_info("Shader linking failed for %s\n", filename); @@ -1927,7 +2004,11 @@ void runShaderPass(GLuint src_texture, GLuint shader_program, GLuint* target_tex if (shader->u_FrameCount >= 0) glUniform1i(shader->u_FrameCount, frame_count); if (shader->u_OutputSize >= 0) glUniform2f(shader->u_OutputSize, dst_width, dst_height); if (shader->u_TextureSize >= 0) glUniform2f(shader->u_TextureSize, shader->texw, shader->texh); + if (shader->OrigInputSize >= 0) glUniform2f(shader->OrigInputSize, shader->srcw, shader->srch); if (shader->u_InputSize >= 0) glUniform2f(shader->u_InputSize, shader->srcw, shader->srch); + for (int i = 0; i < shader->num_pragmas; ++i) { + glUniform1f(shader->pragmas[i].uniformLocation, shader->pragmas[i].value); + } GLint u_MVP = glGetUniformLocation(shader_program, "MVPMatrix"); if (u_MVP >= 0) { diff --git a/workspace/tg5040/platform/platform.c b/workspace/tg5040/platform/platform.c index 806309543..e76bab658 100644 --- a/workspace/tg5040/platform/platform.c +++ b/workspace/tg5040/platform/platform.c @@ -49,8 +49,12 @@ typedef struct Shader { GLint u_OutputSize; GLint u_TextureSize; GLint u_InputSize; + GLint OrigInputSize; GLint texLocation; GLint texelSizeLocation; + ShaderParam *pragmas; // Dynamic array of parsed pragma parameters + int num_pragmas; // Count of valid pragma parameters + } Shader; GLuint g_shader_default = 0; @@ -113,6 +117,44 @@ static uint32_t SDL_transparentBlack = 0; static char* overlay_path = NULL; +#define MAX_SHADERLINE_LENGTH 512 +int extractPragmaParameters(const char *shaderSource, ShaderParam *params, int maxParams) { + const char *pragmaPrefix = "#pragma parameter"; + char line[MAX_SHADERLINE_LENGTH]; + int paramCount = 0; + + const char *currentPos = shaderSource; + + while (*currentPos && paramCount < maxParams) { + int i = 0; + + // Read a line + while (*currentPos && *currentPos != '\n' && i < MAX_SHADERLINE_LENGTH - 1) { + line[i++] = *currentPos++; + } + line[i] = '\0'; + if (*currentPos == '\n') currentPos++; + + // Check if it's a #pragma parameter line + if (strncmp(line, pragmaPrefix, strlen(pragmaPrefix)) == 0) { + const char *start = line + strlen(pragmaPrefix); + while (*start == ' ') start++; + + ShaderParam *p = ¶ms[paramCount]; + + // Try to parse + if (sscanf(start, "%127s \"%127[^\"]\" %f %f %f %f", + p->name, p->label, &p->def, &p->min, &p->max, &p->step) == 6) { + paramCount++; + } else { + fprintf(stderr, "Failed to parse line:\n%s\n", line); + } + } + } + + return paramCount; // number of parameters found +} + GLuint link_program(GLuint vertex_shader, GLuint fragment_shader, const char* cache_key) { char cache_path[512]; snprintf(cache_path, sizeof(cache_path), "/mnt/SDCARD/.shadercache/%s.bin", cache_key); @@ -228,7 +270,8 @@ GLuint load_shader_from_file(GLenum type, const char* filename, const char* path "#else\n" "precision mediump float;\n" "#endif\n" - "#endif\n"; + "#endif\n" + "#define PARAMETER_UNIFORM\n"; } else { fprintf(stderr, "Unsupported shader type\n"); free(source); @@ -512,6 +555,20 @@ char* PLAT_findFileInDir(const char *directory, const char *filename) { return NULL; } +#define MAX_SHADER_PRAGMAS 32 +void loadShaderPragmas(Shader *shader, const char *shaderSource) { + shader->pragmas = calloc(MAX_SHADER_PRAGMAS, sizeof(ShaderParam)); + if (!shader->pragmas) { + fprintf(stderr, "Out of memory allocating pragmas for %s\n", shader->filename); + return; + } + shader->num_pragmas = extractPragmaParameters(shaderSource, shader->pragmas, MAX_SHADER_PRAGMAS); +} + +ShaderParam* PLAT_getShaderPragmas(int i) { + return shaders[i]->pragmas; +} + void PLAT_updateShader(int i, const char *filename, int *scale, int *filter, int *scaletype, int *srctype) { if (i < 0 || i >= nrofshaders) { @@ -522,8 +579,14 @@ void PLAT_updateShader(int i, const char *filename, int *scale, int *filter, int if (filename != NULL) { SDL_GL_MakeCurrent(vid.window, vid.gl_context); LOG_info("loading shader \n"); - GLuint vertex_shader1 = load_shader_from_file(GL_VERTEX_SHADER, filename,SHADERS_FOLDER "/glsl"); - GLuint fragment_shader1 = load_shader_from_file(GL_FRAGMENT_SHADER, filename,SHADERS_FOLDER "/glsl"); + + char filepath[512]; + snprintf(filepath, sizeof(filepath), SHADERS_FOLDER "/glsl/%s",filename); + const char *shaderSource = load_shader_source(filepath); + loadShaderPragmas(shader,shaderSource); + + GLuint vertex_shader1 = load_shader_from_file(GL_VERTEX_SHADER, filename,SHADERS_FOLDER "/glsl"); + GLuint fragment_shader1 = load_shader_from_file(GL_FRAGMENT_SHADER, filename,SHADERS_FOLDER "/glsl"); // Link the shader program if (shader->shader_p != 0) { @@ -537,8 +600,19 @@ void PLAT_updateShader(int i, const char *filename, int *scale, int *filter, int shader->u_OutputSize = glGetUniformLocation( shader->shader_p, "OutputSize"); shader->u_TextureSize = glGetUniformLocation( shader->shader_p, "TextureSize"); shader->u_InputSize = glGetUniformLocation( shader->shader_p, "InputSize"); + shader->OrigInputSize = glGetUniformLocation( shader->shader_p, "OrigInputSize"); shader->texLocation = glGetUniformLocation(shader->shader_p, "Texture"); shader->texelSizeLocation = glGetUniformLocation(shader->shader_p, "texelSize"); + for (int i = 0; i < shader->num_pragmas; ++i) { + shader->pragmas[i].uniformLocation = glGetUniformLocation(shader->shader_p, shader->pragmas[i].name); + shader->pragmas[i].value = shader->pragmas[i].min; + printf("Param: %s = %f (min: %f, max: %f, step: %f)\n", + shader->pragmas[i].name, + shader->pragmas[i].def, + shader->pragmas[i].min, + shader->pragmas[i].max, + shader->pragmas[i].step); + } if (shader->shader_p == 0) { LOG_info("Shader linking failed for %s\n", filename); @@ -1696,7 +1770,11 @@ void runShaderPass(GLuint src_texture, GLuint shader_program, GLuint* target_tex if (shader->u_FrameCount >= 0) glUniform1i(shader->u_FrameCount, frame_count); if (shader->u_OutputSize >= 0) glUniform2f(shader->u_OutputSize, dst_width, dst_height); if (shader->u_TextureSize >= 0) glUniform2f(shader->u_TextureSize, shader->texw, shader->texh); + if (shader->OrigInputSize >= 0) glUniform2f(shader->OrigInputSize, shader->srcw, shader->srch); if (shader->u_InputSize >= 0) glUniform2f(shader->u_InputSize, shader->srcw, shader->srch); + for (int i = 0; i < shader->num_pragmas; ++i) { + glUniform1f(shader->pragmas[i].uniformLocation, shader->pragmas[i].value); + } GLint u_MVP = glGetUniformLocation(shader_program, "MVPMatrix"); if (u_MVP >= 0) {