diff --git a/Makefile.ps3 b/Makefile.ps3 index 114b1cfd33..d587240c35 100644 --- a/Makefile.ps3 +++ b/Makefile.ps3 @@ -56,7 +56,7 @@ PPU_SRCS = fifo_buffer.c \ movie.c \ netplay.c \ ps3/ps3_video_psgl.c \ - gfx/shader_cg.c \ + ps3/shader_cg.c \ gfx/snes_state.c \ patch.c \ audio/hermite.c \ diff --git a/ps3/shader_cg.c b/ps3/shader_cg.c new file mode 100644 index 0000000000..427d317f25 --- /dev/null +++ b/ps3/shader_cg.c @@ -0,0 +1,1219 @@ +/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes. + * Copyright (C) 2010-2012 - Hans-Kristian Arntzen + * + * Some code herein may be based on code found in BSNES. + * + * SSNES 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 Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with SSNES. + * If not, see . + */ + +#include "shader_cg.h" +#include +#include +#include "../general.h" +#include +#include "../compat/strl.h" +#include "../conf/config_file.h" +#include "../gfx/image.h" +#include "../dynamic.h" +#include "../compat/posix_string.h" + +#ifdef HAVE_CONFIGFILE +#include "../gfx/snes_state.h" +#endif + +// Used when we call deactivate() since just unbinding the program didn't seem to work... :( +static const char *stock_cg_program = + "void main_vertex" + "(" + " float4 position : POSITION," + " float2 texCoord : TEXCOORD0," + " float4 color : COLOR," + "" + " uniform float4x4 modelViewProj," + "" + " out float4 oPosition : POSITION," + " out float2 otexCoord : TEXCOORD0," + " out float4 oColor : COLOR" + ")" + "{" + " oPosition = mul(modelViewProj, position);" + " otexCoord = texCoord;" + " oColor = color;" + "}" + "" + "float4 main_fragment(in float4 color : COLOR, float2 tex : TEXCOORD0, uniform sampler2D s0 : TEXUNIT0) : COLOR" + "{" + " return color * tex2D(s0, tex);" + "}"; + +static char *menu_cg_program; + +static CGcontext cgCtx; + +struct cg_fbo_params +{ + CGparameter vid_size_f; + CGparameter tex_size_f; + CGparameter vid_size_v; + CGparameter tex_size_v; + CGparameter tex; + CGparameter coord; +}; + +#define MAX_TEXTURES 8 +#define MAX_VARIABLES 64 +#define PREV_TEXTURES 7 + +struct cg_program +{ + CGprogram vprg; + CGprogram fprg; + CGparameter vid_size_f; + CGparameter tex_size_f; + CGparameter out_size_f; + CGparameter frame_cnt_f; + CGparameter frame_dir_f; + CGparameter vid_size_v; + CGparameter tex_size_v; + CGparameter out_size_v; + CGparameter frame_cnt_v; + CGparameter frame_dir_v; + CGparameter mvp; + + struct cg_fbo_params fbo[SSNES_CG_MAX_SHADERS]; + struct cg_fbo_params orig; + struct cg_fbo_params prev[PREV_TEXTURES]; +}; + +#define FILTER_UNSPEC 0 +#define FILTER_LINEAR 1 +#define FILTER_NEAREST 2 + +static struct cg_program prg[SSNES_CG_MAX_SHADERS]; +static const char **cg_arguments; +static CGprofile cgVProf, cgFProf; +static unsigned active_index = 0; +static unsigned cg_shader_num = 0; +static struct gl_fbo_scale cg_scale[SSNES_CG_MAX_SHADERS]; +static unsigned fbo_smooth[SSNES_CG_MAX_SHADERS]; + +static GLuint lut_textures[MAX_TEXTURES]; +static unsigned lut_textures_num = 0; +static char lut_textures_uniform[MAX_TEXTURES][64]; + +static CGparameter cg_attribs[PREV_TEXTURES + 1 + SSNES_CG_MAX_SHADERS]; +static unsigned cg_attrib_index; + +#ifdef HAVE_CONFIGFILE +static snes_tracker_t *snes_tracker = NULL; +#endif + +static void gl_cg_reset_attrib(void) +{ + for (unsigned i = 0; i < cg_attrib_index; i++) + cgGLDisableClientState(cg_attribs[i]); + cg_attrib_index = 0; +} + +void gl_cg_set_proj_matrix(void) +{ + if (prg[active_index].mvp) + cgGLSetStateMatrixParameter(prg[active_index].mvp, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY); +} + +#define set_param_2f(param, x, y) \ + if (param) cgGLSetParameter2f(param, x, y) +#define set_param_1f(param, x) \ + if (param) cgGLSetParameter1f(param, x) + +void gl_cg_set_params(unsigned width, unsigned height, + unsigned tex_width, unsigned tex_height, + unsigned out_width, unsigned out_height, + unsigned frame_count, + const struct gl_tex_info *info, + const struct gl_tex_info *prev_info, + const struct gl_tex_info *fbo_info, + unsigned fbo_info_cnt) +{ + if (active_index == 0) + return; + + // Set frame. + set_param_2f(prg[active_index].vid_size_f, width, height); + set_param_2f(prg[active_index].tex_size_f, tex_width, tex_height); + set_param_2f(prg[active_index].out_size_f, out_width, out_height); + set_param_1f(prg[active_index].frame_cnt_f, (float)frame_count); + set_param_1f(prg[active_index].frame_dir_f, g_extern.frame_is_reverse ? -1.0 : 1.0); + + set_param_2f(prg[active_index].vid_size_v, width, height); + set_param_2f(prg[active_index].tex_size_v, tex_width, tex_height); + set_param_2f(prg[active_index].out_size_v, out_width, out_height); + set_param_1f(prg[active_index].frame_cnt_v, (float)frame_count); + set_param_1f(prg[active_index].frame_dir_v, g_extern.frame_is_reverse ? -1.0 : 1.0); + + if (active_index == SSNES_CG_MENU_SHADER_INDEX) + return; + + // Set orig texture. + CGparameter param = prg[active_index].orig.tex; + if (param) + { + cgGLSetTextureParameter(param, info->tex); + cgGLEnableTextureParameter(param); + } + + set_param_2f(prg[active_index].orig.vid_size_v, info->input_size[0], info->input_size[1]); + set_param_2f(prg[active_index].orig.vid_size_f, info->input_size[0], info->input_size[1]); + set_param_2f(prg[active_index].orig.tex_size_v, info->tex_size[0], info->tex_size[1]); + set_param_2f(prg[active_index].orig.tex_size_f, info->tex_size[0], info->tex_size[1]); + if (prg[active_index].orig.coord) + { + cgGLSetParameterPointer(prg[active_index].orig.coord, 2, GL_FLOAT, 0, info->coord); + cgGLEnableClientState(prg[active_index].orig.coord); + cg_attribs[cg_attrib_index++] = prg[active_index].orig.coord; + } + + // Set prev textures. + for (unsigned i = 0; i < PREV_TEXTURES; i++) + { + param = prg[active_index].prev[i].tex; + if (param) + { + cgGLSetTextureParameter(param, prev_info[i].tex); + cgGLEnableTextureParameter(param); + } + + set_param_2f(prg[active_index].prev[i].vid_size_v, prev_info[i].input_size[0], prev_info[i].input_size[1]); + set_param_2f(prg[active_index].prev[i].vid_size_f, prev_info[i].input_size[0], prev_info[i].input_size[1]); + set_param_2f(prg[active_index].prev[i].tex_size_v, prev_info[i].tex_size[0], prev_info[i].tex_size[1]); + set_param_2f(prg[active_index].prev[i].tex_size_f, prev_info[i].tex_size[0], prev_info[i].tex_size[1]); + + if (prg[active_index].prev[i].coord) + { + cgGLSetParameterPointer(prg[active_index].prev[i].coord, 2, GL_FLOAT, 0, prev_info[i].coord); + cgGLEnableClientState(prg[active_index].prev[i].coord); + cg_attribs[cg_attrib_index++] = prg[active_index].prev[i].coord; + } + } + + // Set lookup textures. + for (unsigned i = 0; i < lut_textures_num; i++) + { + CGparameter param = cgGetNamedParameter(prg[active_index].fprg, lut_textures_uniform[i]); + if (param) + { + cgGLSetTextureParameter(param, lut_textures[i]); + cgGLEnableTextureParameter(param); + } + } + + // Set FBO textures. + if (active_index > 2) + { + for (unsigned i = 0; i < fbo_info_cnt; i++) + { + if (prg[active_index].fbo[i].tex) + { + cgGLSetTextureParameter(prg[active_index].fbo[i].tex, fbo_info[i].tex); + cgGLEnableTextureParameter(prg[active_index].fbo[i].tex); + } + + set_param_2f(prg[active_index].fbo[i].vid_size_v, fbo_info[i].input_size[0], fbo_info[i].input_size[1]); + set_param_2f(prg[active_index].fbo[i].vid_size_f, fbo_info[i].input_size[0], fbo_info[i].input_size[1]); + + set_param_2f(prg[active_index].fbo[i].tex_size_v, fbo_info[i].tex_size[0], fbo_info[i].tex_size[1]); + set_param_2f(prg[active_index].fbo[i].tex_size_f, fbo_info[i].tex_size[0], fbo_info[i].tex_size[1]); + + if (prg[active_index].fbo[i].coord) + { + cgGLSetParameterPointer(prg[active_index].fbo[i].coord, 2, GL_FLOAT, 0, fbo_info[i].coord); + cgGLEnableClientState(prg[active_index].fbo[i].coord); + cg_attribs[cg_attrib_index++] = prg[active_index].fbo[i].coord; + } + } + } + +#ifdef HAVE_CONFIGFILE + // Set state parameters + if (snes_tracker) + { + static struct snes_tracker_uniform info[MAX_VARIABLES]; + static unsigned cnt = 0; + + if (active_index == 1) + cnt = snes_get_uniform(snes_tracker, info, MAX_VARIABLES, frame_count); + + for (unsigned i = 0; i < cnt; i++) + { + CGparameter param_v = cgGetNamedParameter(prg[active_index].vprg, info[i].id); + CGparameter param_f = cgGetNamedParameter(prg[active_index].fprg, info[i].id); + set_param_1f(param_v, info[i].value); + set_param_1f(param_f, info[i].value); + } + } +#endif +} + +static void gl_cg_deinit_progs(void) +{ + cgGLUnbindProgram(cgFProf); + cgGLUnbindProgram(cgVProf); + + // Programs may alias [0]. + for (unsigned i = 1; i < SSNES_CG_MAX_SHADERS; i++) + { + if (prg[i].fprg != prg[0].fprg) + cgDestroyProgram(prg[i].fprg); + if (prg[i].vprg != prg[0].vprg) + cgDestroyProgram(prg[i].vprg); + } + + if (prg[0].fprg) + cgDestroyProgram(prg[0].fprg); + if (prg[0].vprg) + cgDestroyProgram(prg[0].vprg); + + memset(prg, 0, sizeof(prg)); +} + +static void gl_cg_deinit_state(void) +{ + gl_cg_reset_attrib(); + + cg_shader_num = 0; + + gl_cg_deinit_progs(); + + memset(cg_scale, 0, sizeof(cg_scale)); + memset(fbo_smooth, 0, sizeof(fbo_smooth)); + + glDeleteTextures(lut_textures_num, lut_textures); + lut_textures_num = 0; + +#ifdef HAVE_CONFIGFILE + if (snes_tracker) + { + snes_tracker_free(snes_tracker); + snes_tracker = NULL; + } +#endif +} + +// Final deinit. +static void gl_cg_deinit_context_state(void) +{ + if (menu_cg_program) + { + free(menu_cg_program); + menu_cg_program = NULL; + } +} + +// Full deinit. +void gl_cg_deinit(void) +{ + gl_cg_deinit_state(); + gl_cg_deinit_context_state(); +} + +// Deinit as much as possible without resetting context (broken on PS3), +// and reinit cleanly. +// If this fails, we're kinda screwed without resetting everything on PS3. +bool gl_cg_reinit(const char *path) +{ + gl_cg_deinit_state(); + + return gl_cg_init(path); +} + +#define SET_LISTING(type) \ +{ \ + const char *list = cgGetLastListing(cgCtx); \ + if (list) \ + listing_##type = strdup(list); \ +} + +static bool load_program(unsigned index, const char *prog, bool path_is_file) +{ + bool ret = true; + char *listing_f = NULL; + char *listing_v = NULL; + + if (path_is_file) + { + prg[index].fprg = cgCreateProgramFromFile(cgCtx, CG_SOURCE, prog, cgFProf, "main_fragment", cg_arguments); + SET_LISTING(f); + prg[index].vprg = cgCreateProgramFromFile(cgCtx, CG_SOURCE, prog, cgVProf, "main_vertex", cg_arguments); + SET_LISTING(v); + } + else + { + prg[index].fprg = cgCreateProgram(cgCtx, CG_SOURCE, prog, cgFProf, "main_fragment", cg_arguments); + SET_LISTING(f); + prg[index].vprg = cgCreateProgram(cgCtx, CG_SOURCE, prog, cgVProf, "main_vertex", cg_arguments); + SET_LISTING(v); + } + + if (!prg[index].fprg || !prg[index].vprg) + { + SSNES_ERR("CG error: %s\n", cgGetErrorString(cgGetError())); + if (listing_f) + SSNES_ERR("Fragment:\n%s\n", listing_f); + else if (listing_v) + SSNES_ERR("Vertex:\n%s\n", listing_v); + + ret = false; + goto end; + } + + cgGLLoadProgram(prg[index].fprg); + cgGLLoadProgram(prg[index].vprg); + +end: + free(listing_f); + free(listing_v); + return ret; +} + +static bool load_stock(void) +{ + if (!load_program(0, stock_cg_program, false)) + { + SSNES_ERR("Failed to compile passthrough shader, is something wrong with your environment?\n"); + return false; + } + + return true; +} + +static bool load_plain(const char *path) +{ + if (!load_stock()) + return false; + + SSNES_LOG("Loading Cg file: %s\n", path); + + if (!load_program(1, path, true)) + return false; + + if (*g_settings.video.second_pass_shader && g_settings.video.render_to_texture) + { + if (!load_program(2, g_settings.video.second_pass_shader, true)) + return false; + + cg_shader_num = 2; + } + else + { + prg[2] = prg[0]; + cg_shader_num = 1; + } + + return true; +} + +static bool load_menu_shader(void) +{ + return load_program(SSNES_CG_MENU_SHADER_INDEX, menu_cg_program, true); +} + +#define print_buf(buf, ...) snprintf(buf, sizeof(buf), __VA_ARGS__) + +#ifdef HAVE_CONFIGFILE +static void load_texture_data(GLuint *obj, const struct texture_image *img, bool smooth) +{ + glGenTextures(1, obj); + glBindTexture(GL_TEXTURE_2D, *obj); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, smooth ? GL_LINEAR : GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, smooth ? GL_LINEAR : GL_NEAREST); + + glTexImage2D(GL_TEXTURE_2D, + 0, GL_ARGB_SCE, img->width, img->height, + 0, GL_ARGB_SCE, GL_UNSIGNED_INT_8_8_8_8, img->pixels); + + free(img->pixels); +} + +static bool load_textures(const char *dir_path, config_file_t *conf) +{ + bool ret = true; + char *textures = NULL; + if (!config_get_string(conf, "textures", &textures)) // No textures here ... + return true; + + const char *id = strtok(textures, ";");; + while (id && lut_textures_num < MAX_TEXTURES) + { + char path[PATH_MAX]; + if (!config_get_array(conf, id, path, sizeof(path))) + { + SSNES_ERR("Cannot find path to texture \"%s\" ...\n", id); + ret = false; + goto end; + } + + char id_filter[64]; + print_buf(id_filter, "%s_linear", id); + + bool smooth = true; + if (!config_get_bool(conf, id_filter, &smooth)) + smooth = true; + + char id_absolute[64]; + print_buf(id_absolute, "%s_absolute", id); + + bool absolute = false; + if (!config_get_bool(conf, id_absolute, &absolute)) + absolute = false; + + char image_path[512]; + if (absolute) + print_buf(image_path, "%s", path); + else + print_buf(image_path, "%s%s", dir_path, path); + + SSNES_LOG("Loading image from: \"%s\".\n", image_path); + struct texture_image img; + if (!texture_image_load(image_path, &img)) + { + SSNES_ERR("Failed to load picture ...\n"); + ret = false; + goto end; + } + + strlcpy(lut_textures_uniform[lut_textures_num], + id, sizeof(lut_textures_uniform[lut_textures_num])); + + load_texture_data(&lut_textures[lut_textures_num], &img, smooth); + lut_textures_num++; + + id = strtok(NULL, ";"); + } + +end: + free(textures); + glBindTexture(GL_TEXTURE_2D, 0); + return ret; +} + +static bool load_imports(const char *dir_path, config_file_t *conf) +{ + bool ret = true; + char *imports = NULL; + + if (!config_get_string(conf, "imports", &imports)) + return true; + + struct snes_tracker_uniform_info info[MAX_VARIABLES]; + unsigned info_cnt = 0; + struct snes_tracker_info tracker_info = {0}; + + const char *id = strtok(imports, ";"); + while (id && info_cnt < MAX_VARIABLES) + { + char semantic_buf[64]; + char wram_buf[64]; + char input_slot_buf[64]; + char apuram_buf[64]; + char oam_buf[64]; + char cgram_buf[64]; + char vram_buf[64]; + char mask_buf[64]; + char equal_buf[64]; + + print_buf(semantic_buf, "%s_semantic", id); + print_buf(wram_buf, "%s_wram", id); + print_buf(input_slot_buf, "%s_input_slot", id); + print_buf(apuram_buf, "%s_apuram", id); + print_buf(oam_buf, "%s_oam", id); + print_buf(cgram_buf, "%s_cgram", id); + print_buf(vram_buf, "%s_vram", id); + print_buf(mask_buf, "%s_mask", id); + print_buf(equal_buf, "%s_equal", id); + + char *semantic = NULL; + + config_get_string(conf, semantic_buf, &semantic); + + if (!semantic) + { + SSNES_ERR("No semantic for import variable.\n"); + ret = false; + goto end; + } + + enum snes_tracker_type tracker_type; + enum snes_ram_type ram_type = SSNES_STATE_NONE; + + if (strcmp(semantic, "capture") == 0) + tracker_type = SSNES_STATE_CAPTURE; + else if (strcmp(semantic, "transition") == 0) + tracker_type = SSNES_STATE_TRANSITION; + else if (strcmp(semantic, "transition_count") == 0) + tracker_type = SSNES_STATE_TRANSITION_COUNT; + else if (strcmp(semantic, "capture_previous") == 0) + tracker_type = SSNES_STATE_CAPTURE_PREV; + else if (strcmp(semantic, "transition_previous") == 0) + tracker_type = SSNES_STATE_TRANSITION_PREV; + else + { + SSNES_ERR("Invalid semantic.\n"); + ret = false; + goto end; + } + + unsigned addr = 0; + unsigned input_slot = 0; + if (config_get_hex(conf, input_slot_buf, &input_slot)) + { + switch (input_slot) + { + case 1: + ram_type = SSNES_STATE_INPUT_SLOT1; + break; + + case 2: + ram_type = SSNES_STATE_INPUT_SLOT2; + break; + + default: + SSNES_ERR("Invalid input slot for import.\n"); + ret = false; + goto end; + } + } + else if (config_get_hex(conf, wram_buf, &addr)) + ram_type = SSNES_STATE_WRAM; + else if (config_get_hex(conf, apuram_buf, &addr)) + ram_type = SSNES_STATE_APURAM; + else if (config_get_hex(conf, oam_buf, &addr)) + ram_type = SSNES_STATE_OAM; + else if (config_get_hex(conf, cgram_buf, &addr)) + ram_type = SSNES_STATE_CGRAM; + else if (config_get_hex(conf, vram_buf, &addr)) + ram_type = SSNES_STATE_VRAM; + else + { + SSNES_ERR("No address assigned to semantic.\n"); + ret = false; + goto end; + } + + unsigned memtype; + switch (ram_type) + { + case SSNES_STATE_WRAM: + memtype = SNES_MEMORY_WRAM; + break; + case SSNES_STATE_APURAM: + memtype = SNES_MEMORY_APURAM; + break; + case SSNES_STATE_VRAM: + memtype = SNES_MEMORY_VRAM; + break; + case SSNES_STATE_OAM: + memtype = SNES_MEMORY_OAM; + break; + case SSNES_STATE_CGRAM: + memtype = SNES_MEMORY_CGRAM; + break; + + default: + memtype = -1u; + } + + if ((memtype != -1u) && (addr >= psnes_get_memory_size(memtype))) + { + SSNES_ERR("Address out of bounds.\n"); + ret = false; + goto end; + } + + unsigned bitmask; + if (!config_get_hex(conf, mask_buf, &bitmask)) + bitmask = 0; + unsigned bitequal; + if (!config_get_hex(conf, equal_buf, &bitequal)) + bitequal = 0; + + strlcpy(info[info_cnt].id, id, sizeof(info[info_cnt].id)); + info[info_cnt].addr = addr; + info[info_cnt].type = tracker_type; + info[info_cnt].ram_type = ram_type; + info[info_cnt].mask = bitmask; + info[info_cnt].equal = bitequal; + + info_cnt++; + free(semantic); + + id = strtok(NULL, ";"); + } + + tracker_info.wram = psnes_get_memory_data(SNES_MEMORY_WRAM); + tracker_info.vram = psnes_get_memory_data(SNES_MEMORY_VRAM); + tracker_info.cgram = psnes_get_memory_data(SNES_MEMORY_CGRAM); + tracker_info.oam = psnes_get_memory_data(SNES_MEMORY_OAM); + tracker_info.apuram = psnes_get_memory_data(SNES_MEMORY_APURAM); + tracker_info.info = info; + tracker_info.info_elem = info_cnt; + + snes_tracker = snes_tracker_init(&tracker_info); + if (!snes_tracker) + SSNES_WARN("Failed to init SNES tracker.\n"); + +end: + free(imports); + return ret; +} +#endif + +static bool load_shader(const char *dir_path, unsigned i, config_file_t *conf) +{ + char *shader_path; + char attr_buf[64]; + char path_buf[PATH_MAX]; + + print_buf(attr_buf, "shader%u", i); + if (config_get_string(conf, attr_buf, &shader_path)) + { + strlcpy(path_buf, dir_path, sizeof(path_buf)); + strlcat(path_buf, shader_path, sizeof(path_buf)); + free(shader_path); + } + else + { + SSNES_ERR("Didn't find shader path in config ...\n"); + return false; + } + + SSNES_LOG("Loading Cg shader: \"%s\".\n", path_buf); + + if (!load_program(i + 1, path_buf, true)) + return false; + + return true; +} + +static bool load_shader_params(unsigned i, config_file_t *conf) +{ + bool ret = true; + char *scale_type = NULL; + char *scale_type_x = NULL; + char *scale_type_y = NULL; + bool has_scale_type; + bool has_scale_type_x; + bool has_scale_type_y; + + char scale_name_buf[64]; + print_buf(scale_name_buf, "scale_type%u", i); + has_scale_type = config_get_string(conf, scale_name_buf, &scale_type); + print_buf(scale_name_buf, "scale_type_x%u", i); + has_scale_type_x = config_get_string(conf, scale_name_buf, &scale_type_x); + print_buf(scale_name_buf, "scale_type_y%u", i); + has_scale_type_y = config_get_string(conf, scale_name_buf, &scale_type_y); + + if (!has_scale_type && !has_scale_type_x && !has_scale_type_y) + return true; + + if (has_scale_type) + { + free(scale_type_x); + free(scale_type_y); + + scale_type_x = strdup(scale_type); + scale_type_y = strdup(scale_type); + + free(scale_type); + scale_type = NULL; + } + + char attr_name_buf[64]; + float fattr; + int iattr; + struct gl_fbo_scale *scale = &cg_scale[i + 1]; // Shader 0 is passthrough shader. Start at 1. + + scale->valid = true; + scale->type_x = SSNES_SCALE_INPUT; + scale->type_y = SSNES_SCALE_INPUT; + scale->scale_x = 1.0; + scale->scale_y = 1.0; + scale->abs_x = g_extern.system.geom.base_width; + scale->abs_y = g_extern.system.geom.base_height; + + if (strcmp(scale_type_x, "source") == 0) + scale->type_x = SSNES_SCALE_INPUT; + else if (strcmp(scale_type_x, "viewport") == 0) + scale->type_x = SSNES_SCALE_VIEWPORT; + else if (strcmp(scale_type_x, "absolute") == 0) + scale->type_x = SSNES_SCALE_ABSOLUTE; + else + { + SSNES_ERR("Invalid attribute.\n"); + ret = false; + goto end; + } + + if (strcmp(scale_type_y, "source") == 0) + scale->type_y = SSNES_SCALE_INPUT; + else if (strcmp(scale_type_y, "viewport") == 0) + scale->type_y = SSNES_SCALE_VIEWPORT; + else if (strcmp(scale_type_y, "absolute") == 0) + scale->type_y = SSNES_SCALE_ABSOLUTE; + else + { + SSNES_ERR("Invalid attribute.\n"); + ret = false; + goto end; + } + + if (scale->type_x == SSNES_SCALE_ABSOLUTE) + { + print_buf(attr_name_buf, "scale%u", i); + if (config_get_int(conf, attr_name_buf, &iattr)) + scale->abs_x = iattr; + else + { + print_buf(attr_name_buf, "scale_x%u", i); + if (config_get_int(conf, attr_name_buf, &iattr)) + scale->abs_x = iattr; + } + } + else + { + print_buf(attr_name_buf, "scale%u", i); + if (config_get_float(conf, attr_name_buf, &fattr)) + scale->scale_x = fattr; + else + { + print_buf(attr_name_buf, "scale_x%u", i); + if (config_get_float(conf, attr_name_buf, &fattr)) + scale->scale_x = fattr; + } + } + + if (scale->type_y == SSNES_SCALE_ABSOLUTE) + { + print_buf(attr_name_buf, "scale%u", i); + if (config_get_int(conf, attr_name_buf, &iattr)) + scale->abs_y = iattr; + else + { + print_buf(attr_name_buf, "scale_y%u", i); + if (config_get_int(conf, attr_name_buf, &iattr)) + scale->abs_y = iattr; + } + } + else + { + print_buf(attr_name_buf, "scale%u", i); + if (config_get_float(conf, attr_name_buf, &fattr)) + scale->scale_y = fattr; + else + { + print_buf(attr_name_buf, "scale_y%u", i); + if (config_get_float(conf, attr_name_buf, &fattr)) + scale->scale_y = fattr; + } + } + +end: + free(scale_type); + free(scale_type_x); + free(scale_type_y); + return ret; +} + +static bool load_preset(const char *path) +{ +#ifdef HAVE_CONFIGFILE + bool ret = true; + + if (!load_stock()) + return false; + + int shaders; + // Basedir. + char dir_path[PATH_MAX]; + char *ptr = NULL; + + SSNES_LOG("Loading Cg meta-shader: %s\n", path); + config_file_t *conf = config_file_new(path); + if (!conf) + { + SSNES_ERR("Failed to load preset.\n"); + ret = false; + goto end; + } + + if (!config_get_int(conf, "shaders", &shaders)) + { + SSNES_ERR("Cannot find \"shaders\" param.\n"); + ret = false; + goto end; + } + + if (shaders < 1) + { + SSNES_ERR("Need to define at least 1 shader.\n"); + ret = false; + goto end; + } + + cg_shader_num = shaders; + if (shaders > SSNES_CG_MAX_SHADERS - 3) + { + SSNES_WARN("Too many shaders ... Capping shader amount to %d.\n", SSNES_CG_MAX_SHADERS - 3); + cg_shader_num = shaders = SSNES_CG_MAX_SHADERS - 3; + } + // If we aren't using last pass non-FBO shader, + // this shader will be assumed to be "fixed-function". + // Just use prg[0] for that pass, which will be + // pass-through. + prg[shaders + 1] = prg[0]; + + // Check filter params. + for (int i = 0; i < shaders; i++) + { + bool smooth; + char filter_name_buf[64]; + print_buf(filter_name_buf, "filter_linear%u", i); + if (config_get_bool(conf, filter_name_buf, &smooth)) + fbo_smooth[i + 1] = smooth ? FILTER_LINEAR : FILTER_NEAREST; + } + + strlcpy(dir_path, path, sizeof(dir_path)); + ptr = strrchr(dir_path, '/'); + if (!ptr) ptr = strrchr(dir_path, '\\'); + if (ptr) + ptr[1] = '\0'; + else // No directory. + dir_path[0] = '\0'; + + for (int i = 0; i < shaders; i++) + { + if (!load_shader_params(i, conf)) + { + SSNES_ERR("Failed to load shader params ...\n"); + ret = false; + goto end; + } + + if (!load_shader(dir_path, i, conf)) + { + SSNES_ERR("Failed to load shaders ...\n"); + ret = false; + goto end; + } + } + + if (!load_textures(dir_path, conf)) + { + SSNES_ERR("Failed to load lookup textures ...\n"); + ret = false; + goto end; + } + + if (!load_imports(dir_path, conf)) + { + SSNES_ERR("Failed to load imports ...\n"); + ret = false; + goto end; + } + +end: + if (conf) + config_file_free(conf); + return ret; + +#else + (void)path; + SSNES_ERR("No config file support compiled in.\n"); + return false; +#endif +} + +static void set_program_attributes(unsigned i) +{ + cgGLBindProgram(prg[i].fprg); + cgGLBindProgram(prg[i].vprg); + + prg[i].vid_size_f = cgGetNamedParameter(prg[i].fprg, "IN.video_size"); + prg[i].tex_size_f = cgGetNamedParameter(prg[i].fprg, "IN.texture_size"); + prg[i].out_size_f = cgGetNamedParameter(prg[i].fprg, "IN.output_size"); + prg[i].frame_cnt_f = cgGetNamedParameter(prg[i].fprg, "IN.frame_count"); + prg[i].frame_dir_f = cgGetNamedParameter(prg[i].fprg, "IN.frame_direction"); + prg[i].vid_size_v = cgGetNamedParameter(prg[i].vprg, "IN.video_size"); + prg[i].tex_size_v = cgGetNamedParameter(prg[i].vprg, "IN.texture_size"); + prg[i].out_size_v = cgGetNamedParameter(prg[i].vprg, "IN.output_size"); + prg[i].frame_cnt_v = cgGetNamedParameter(prg[i].vprg, "IN.frame_count"); + prg[i].frame_dir_v = cgGetNamedParameter(prg[i].vprg, "IN.frame_direction"); + prg[i].mvp = cgGetNamedParameter(prg[i].vprg, "modelViewProj"); + if (prg[i].mvp) + cgGLSetStateMatrixParameter(prg[i].mvp, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY); + + if (i == SSNES_CG_MENU_SHADER_INDEX) + return; + + prg[i].orig.tex = cgGetNamedParameter(prg[i].fprg, "ORIG.texture"); + prg[i].orig.vid_size_v = cgGetNamedParameter(prg[i].vprg, "ORIG.video_size"); + prg[i].orig.vid_size_f = cgGetNamedParameter(prg[i].fprg, "ORIG.video_size"); + prg[i].orig.tex_size_v = cgGetNamedParameter(prg[i].vprg, "ORIG.texture_size"); + prg[i].orig.tex_size_f = cgGetNamedParameter(prg[i].fprg, "ORIG.texture_size"); + prg[i].orig.coord = cgGetNamedParameter(prg[i].vprg, "ORIG.tex_coord"); + + for (unsigned j = 0; j < PREV_TEXTURES; j++) + { + char attr_buf_tex[64]; + char attr_buf_vid_size[64]; + char attr_buf_tex_size[64]; + char attr_buf_coord[64]; + static const char *prev_names[PREV_TEXTURES] = { + "PREV", + "PREV1", + "PREV2", + "PREV3", + "PREV4", + "PREV5", + "PREV6", + }; + + snprintf(attr_buf_tex, sizeof(attr_buf_tex), "%s.texture", prev_names[j]); + snprintf(attr_buf_vid_size, sizeof(attr_buf_vid_size), "%s.video_size", prev_names[j]); + snprintf(attr_buf_tex_size, sizeof(attr_buf_tex_size), "%s.texture_size", prev_names[j]); + snprintf(attr_buf_coord, sizeof(attr_buf_coord), "%s.tex_coord", prev_names[j]); + + prg[i].prev[j].tex = cgGetNamedParameter(prg[i].fprg, attr_buf_tex); + + prg[i].prev[j].vid_size_v = cgGetNamedParameter(prg[i].vprg, attr_buf_vid_size); + prg[i].prev[j].vid_size_f = cgGetNamedParameter(prg[i].fprg, attr_buf_vid_size); + + prg[i].prev[j].tex_size_v = cgGetNamedParameter(prg[i].vprg, attr_buf_tex_size); + prg[i].prev[j].tex_size_f = cgGetNamedParameter(prg[i].fprg, attr_buf_tex_size); + + prg[i].prev[j].coord = cgGetNamedParameter(prg[i].vprg, attr_buf_coord); + } + + for (unsigned j = 0; j < i - 1; j++) + { + char attr_buf[64]; + + snprintf(attr_buf, sizeof(attr_buf), "PASS%u.texture", j + 1); + prg[i].fbo[j].tex = cgGetNamedParameter(prg[i].fprg, attr_buf); + + snprintf(attr_buf, sizeof(attr_buf), "PASS%u.video_size", j + 1); + prg[i].fbo[j].vid_size_v = cgGetNamedParameter(prg[i].vprg, attr_buf); + prg[i].fbo[j].vid_size_f = cgGetNamedParameter(prg[i].fprg, attr_buf); + + snprintf(attr_buf, sizeof(attr_buf), "PASS%u.texture_size", j + 1); + prg[i].fbo[j].tex_size_v = cgGetNamedParameter(prg[i].vprg, attr_buf); + prg[i].fbo[j].tex_size_f = cgGetNamedParameter(prg[i].fprg, attr_buf); + + snprintf(attr_buf, sizeof(attr_buf), "PASS%u.tex_coord", j + 1); + prg[i].fbo[j].coord = cgGetNamedParameter(prg[i].vprg, attr_buf); + } +} + +bool gl_cg_init(const char *path) +{ + cgRTCgcInit(); + + if (!cgCtx) + cgCtx = cgCreateContext(); + + if (cgCtx == NULL) + { + SSNES_ERR("Failed to create Cg context\n"); + return false; + } + + cgFProf = cgGLGetLatestProfile(CG_GL_FRAGMENT); + cgVProf = cgGLGetLatestProfile(CG_GL_VERTEX); + if (cgFProf == CG_PROFILE_UNKNOWN || cgVProf == CG_PROFILE_UNKNOWN) + { + SSNES_ERR("Invalid profile type\n"); + return false; + } + cgGLSetOptimalOptions(cgFProf); + cgGLSetOptimalOptions(cgVProf); + cgGLEnableProfile(cgFProf); + cgGLEnableProfile(cgVProf); + + if (strstr(path, ".cgp")) + { + if (!load_preset(path)) + return false; + } + else + { + if (!load_plain(path)) + return false; + } + + if (menu_cg_program && !load_menu_shader()) + return false; + + prg[0].mvp = cgGetNamedParameter(prg[0].vprg, "modelViewProj"); + if (prg[0].mvp) + cgGLSetStateMatrixParameter(prg[0].mvp, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY); + + for (unsigned i = 1; i <= cg_shader_num; i++) + set_program_attributes(i); + + if (menu_cg_program) + set_program_attributes(SSNES_CG_MENU_SHADER_INDEX); + + cgGLBindProgram(prg[1].fprg); + cgGLBindProgram(prg[1].vprg); + + return true; +} + +void gl_cg_use(unsigned index) +{ + if (prg[index].vprg && prg[index].fprg) + { + gl_cg_reset_attrib(); + + active_index = index; + cgGLBindProgram(prg[index].vprg); + cgGLBindProgram(prg[index].fprg); + } +} + +unsigned gl_cg_num(void) +{ + return cg_shader_num; +} + +bool gl_cg_filter_type(unsigned index, bool *smooth) +{ + if (fbo_smooth[index] == FILTER_UNSPEC) + return false; + *smooth = (fbo_smooth[index] == FILTER_LINEAR); + return true; +} + +void gl_cg_shader_scale(unsigned index, struct gl_fbo_scale *scale) +{ + *scale = cg_scale[index]; +} + +void gl_cg_set_menu_shader(const char *path) +{ + if (menu_cg_program) + free(menu_cg_program); + + menu_cg_program = strdup(path); +} + +void gl_cg_set_compiler_args(const char **argv) +{ + cg_arguments = argv; +} + +bool gl_cg_load_shader(unsigned index, const char *path) +{ + if (index == 0) + return false; + + if (prg[index].fprg) + { + cgGLUnbindProgram(cgFProf); + + if (prg[0].fprg != prg[index].fprg) + cgDestroyProgram(prg[index].fprg); + } + + if (prg[index].vprg) + { + cgGLUnbindProgram(cgVProf); + + if (prg[0].vprg != prg[index].vprg) + cgDestroyProgram(prg[index].vprg); + } + + memset(&prg[index], 0, sizeof(prg[index])); + + if (load_program(index, path, true)) + { + set_program_attributes(index); + return true; + } + else + { + // Always make sure we have a valid shader. + memcpy(&prg[index], &prg[0], sizeof(prg[0])); + return false; + } +} + +bool gl_cg_save_cgp(const char *path, const struct gl_cg_cgp_info *info) +{ + if (!info->shader[0] || !*info->shader[0]) + return false; + + FILE *file = fopen(path, "w"); + if (!file) + return false; + + unsigned shaders = info->shader[1] && *info->shader[1] ? 2 : 1; + fprintf(file, "shaders = %u\n", shaders); + + fprintf(file, "shader0 = \"%s\"\n", info->shader[0]); + if (shaders == 2) + fprintf(file, "shader1 = \"%s\"\n", info->shader[1]); + + fprintf(file, "filter_linear0 = %s\n", info->filter_linear[0] ? "true" : "false"); + + if (info->render_to_texture) + { + fprintf(file, "filter_linear1 = %s\n", info->filter_linear[1] ? "true" : "false"); + fprintf(file, "scale_type0 = source\n"); + fprintf(file, "scale0 = %.1f\n", info->fbo_scale); + } + + if (info->lut_texture_path && info->lut_texture_id) + { + fprintf(file, "textures = %s\n", info->lut_texture_id); + fprintf(file, "%s = \"%s\"\n", + info->lut_texture_id, info->lut_texture_path); + + fprintf(file, "%s_absolute = %s\n", + info->lut_texture_id, + info->lut_texture_absolute ? "true" : "false"); + } + + fclose(file); + return true; +} + +void gl_cg_invalidate_context(void) +{ + cgCtx = NULL; +} + +unsigned gl_cg_get_lut_info(struct gl_cg_lut_info *info, unsigned elems) +{ + elems = elems > lut_textures_num ? lut_textures_num : elems; + + for (unsigned i = 0; i < elems; i++) + { + strlcpy(info[i].id, lut_textures_uniform[i], sizeof(info[i].id)); + info[i].tex = lut_textures[i]; + } + + return elems; +} + diff --git a/ps3/shader_cg.h b/ps3/shader_cg.h new file mode 100644 index 0000000000..bb69cc8c38 --- /dev/null +++ b/ps3/shader_cg.h @@ -0,0 +1,81 @@ +/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes. + * Copyright (C) 2010-2012 - Hans-Kristian Arntzen + * + * Some code herein may be based on code found in BSNES. + * + * SSNES 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 Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with SSNES. + * If not, see . + */ + + +#ifndef __SSNES_CG_H +#define __SSNES_CG_H + +#include "../boolean.h" +#include "../gfx/gl_common.h" +#include + +bool gl_cg_init(const char *path); +bool gl_cg_reinit(const char *path); + +void gl_cg_deinit(void); + +void gl_cg_set_proj_matrix(void); + +void gl_cg_set_params(unsigned width, unsigned height, + unsigned tex_width, unsigned tex_height, + unsigned out_width, unsigned out_height, + unsigned frame_count, + const struct gl_tex_info *info, + const struct gl_tex_info *prev_info, + const struct gl_tex_info *fbo_info, + unsigned fbo_info_cnt); + +void gl_cg_use(unsigned index); + +unsigned gl_cg_num(void); + +bool gl_cg_filter_type(unsigned index, bool *smooth); +void gl_cg_shader_scale(unsigned index, struct gl_fbo_scale *scale); + +// Used on PS3, but not really platform specific. + +#define SSNES_CG_MAX_SHADERS 16 +#define SSNES_CG_MENU_SHADER_INDEX (SSNES_CG_MAX_SHADERS - 1) +void gl_cg_set_menu_shader(const char *path); +void gl_cg_set_compiler_args(const char **argv); + +bool gl_cg_load_shader(unsigned index, const char *path); + +struct gl_cg_cgp_info +{ + const char *shader[2]; + bool filter_linear[2]; + bool render_to_texture; + float fbo_scale; + + const char *lut_texture_path; + const char *lut_texture_id; + bool lut_texture_absolute; +}; + +bool gl_cg_save_cgp(const char *path, const struct gl_cg_cgp_info *info); +void gl_cg_invalidate_context(void); // Call when resetting GL context on PS3. + +struct gl_cg_lut_info +{ + char id[64]; + GLuint tex; +}; + +unsigned gl_cg_get_lut_info(struct gl_cg_lut_info *info, unsigned elems); + +#endif