/* RetroArch - A frontend for libretro. * Copyright (C) 2010-2014 - Hans-Kristian Arntzen * * RetroArch 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. * * RetroArch 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 RetroArch. * If not, see . */ #ifdef _MSC_VER #pragma comment(lib, "cg") #pragma comment(lib, "cggl") #endif #include "shader_cg.h" #include "shader_common.h" #include #include #include "../general.h" #include #include "../compat/strl.h" #include "../conf/config_file.h" #include "image.h" #include "../dynamic.h" #include "../compat/posix_string.h" #include "../file.h" #include "state_tracker.h" //#define RARCH_CG_DEBUG // Used when we call deactivate() since just unbinding the program didn't seem to work... :( static const char *stock_cg_program = "struct input" "{" " float2 tex_coord;" " float4 color;" " float4 vertex_coord;" " uniform float4x4 mvp_matrix;" " uniform sampler2D texture;" "};" "struct vertex_data" "{" " float2 tex;" " float4 color;" "};" "void main_vertex" "(" " out float4 oPosition : POSITION," " input IN," " out vertex_data vert" ")" "{" " oPosition = mul(IN.mvp_matrix, IN.vertex_coord);" " vert = vertex_data(IN.tex_coord, IN.color);" "}" "" "float4 main_fragment(input IN, vertex_data vert, uniform sampler2D s0 : TEXUNIT0) : COLOR" "{" " return vert.color * tex2D(s0, vert.tex);" "}"; #ifdef RARCH_CG_DEBUG static void cg_error_handler(CGcontext ctx, CGerror error, void *data) { (void)ctx; (void)data; switch (error) { case CG_INVALID_PARAM_HANDLE_ERROR: RARCH_ERR("CG: Invalid param handle.\n"); break; case CG_INVALID_PARAMETER_ERROR: RARCH_ERR("CG: Invalid parameter.\n"); break; default: break; } RARCH_ERR("CG error: \"%s\"\n", cgGetErrorString(error)); } #endif 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_LUT_TEXTURES 8 #define MAX_VARIABLES 64 #define PREV_TEXTURES (MAX_TEXTURES - 1) struct cg_program { CGprogram vprg; CGprogram fprg; CGparameter tex; CGparameter lut_tex; CGparameter color; CGparameter vertex; 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[GFX_MAX_SHADERS]; struct cg_fbo_params orig; struct cg_fbo_params prev[PREV_TEXTURES]; }; static struct cg_program prg[GFX_MAX_SHADERS]; static const char **cg_arguments; static bool cg_active; static CGprofile cgVProf, cgFProf; static unsigned active_index; static struct gfx_shader *cg_shader; static state_tracker_t *state_tracker; static GLuint lut_textures[MAX_LUT_TEXTURES]; static CGparameter cg_attribs[PREV_TEXTURES + 1 + 4 + GFX_MAX_SHADERS]; static unsigned cg_attrib_index; static void gl_cg_reset_attrib(void) { unsigned i; for (i = 0; i < cg_attrib_index; i++) cgGLDisableClientState(cg_attribs[i]); cg_attrib_index = 0; } static bool gl_cg_set_mvp(const math_matrix *mat) { if (cg_active && prg[active_index].mvp) { cgGLSetMatrixParameterfc(prg[active_index].mvp, mat->data); return true; } else return false; } #define SET_COORD(name, coords_name, len) do { \ if (prg[active_index].name) \ { \ cgGLSetParameterPointer(prg[active_index].name, len, GL_FLOAT, 0, coords->coords_name); \ cgGLEnableClientState(prg[active_index].name); \ cg_attribs[cg_attrib_index++] = prg[active_index].name; \ } \ } while(0) static bool gl_cg_set_coords(const struct gl_coords *coords) { if (!cg_active) return false; SET_COORD(vertex, vertex, 2); SET_COORD(tex, tex_coord, 2); SET_COORD(lut_tex, lut_tex_coord, 2); SET_COORD(color, color, 4); return true; } #define set_param_2f(param, x, y) \ if (param) cgGLSetParameter2f(param, x, y) #define set_param_1f(param, x) \ if (param) cgGLSetParameter1f(param, x) static 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) { unsigned i; if (!cg_active || (active_index == 0) || (active_index == GL_SHADER_STOCK_BLEND)) 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_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_dir_v, g_extern.frame_is_reverse ? -1.0 : 1.0); if (prg[active_index].frame_cnt_f || prg[active_index].frame_cnt_v) { unsigned modulo = cg_shader->pass[active_index - 1].frame_count_mod; if (modulo) frame_count %= modulo; set_param_1f(prg[active_index].frame_cnt_f, (float)frame_count); set_param_1f(prg[active_index].frame_cnt_v, (float)frame_count); } // 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 (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 (i = 0; i < cg_shader->luts; i++) { CGparameter fparam = cgGetNamedParameter(prg[active_index].fprg, cg_shader->lut[i].id); if (fparam) { cgGLSetTextureParameter(fparam, lut_textures[i]); cgGLEnableTextureParameter(fparam); } CGparameter vparam = cgGetNamedParameter(prg[active_index].vprg, cg_shader->lut[i].id); if (vparam) { cgGLSetTextureParameter(vparam, lut_textures[i]); cgGLEnableTextureParameter(vparam); } } // Set FBO textures. if (active_index > 2) { for (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; } } } // Set state parameters if (state_tracker) { // Only query uniforms in first pass. static struct state_tracker_uniform info[MAX_VARIABLES]; static unsigned cnt = 0; if (active_index == 1) cnt = state_get_uniform(state_tracker, info, MAX_VARIABLES, frame_count); for (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); } } } static void gl_cg_deinit_progs(void) { unsigned i; RARCH_LOG("CG: Destroying programs.\n"); cgGLUnbindProgram(cgFProf); cgGLUnbindProgram(cgVProf); // Programs may alias [0]. for (i = 1; i < GFX_MAX_SHADERS; i++) { if (prg[i].fprg && prg[i].fprg != prg[0].fprg) cgDestroyProgram(prg[i].fprg); if (prg[i].vprg && 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_active = false; gl_cg_deinit_progs(); if (cg_shader && cg_shader->luts) { glDeleteTextures(cg_shader->luts, lut_textures); memset(lut_textures, 0, sizeof(lut_textures)); } if (state_tracker) { state_tracker_free(state_tracker); state_tracker = NULL; } free(cg_shader); cg_shader = NULL; } // Final deinit. static void gl_cg_deinit_context_state(void) { // Destroying context breaks on PS3 for some unknown reason. #ifndef __CELLOS_LV2__ if (cgCtx) { RARCH_LOG("CG: Destroying context.\n"); cgDestroyContext(cgCtx); cgCtx = NULL; } #endif } // Full deinit. static void gl_cg_deinit(void) { gl_cg_deinit_state(); gl_cg_deinit_context_state(); } static bool gl_cg_init(const char *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) { RARCH_ERR("CG error: %s\n", cgGetErrorString(cgGetError())); if (listing_f) RARCH_ERR("Fragment:\n%s\n", listing_f); else if (listing_v) RARCH_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 void set_program_base_attrib(unsigned i); static bool load_stock(void) { if (!load_program(0, stock_cg_program, false)) { RARCH_ERR("Failed to compile passthrough shader, is something wrong with your environment?\n"); return false; } set_program_base_attrib(0); return true; } static bool load_plain(const char *path) { if (!load_stock()) return false; cg_shader = (struct gfx_shader*)calloc(1, sizeof(*cg_shader)); if (!cg_shader) return false; cg_shader->passes = 1; if (path) { RARCH_LOG("Loading Cg file: %s\n", path); strlcpy(cg_shader->pass[0].source.cg, path, sizeof(cg_shader->pass[0].source.cg)); if (!load_program(1, path, true)) return false; } else { RARCH_LOG("Loading stock Cg file.\n"); prg[1] = prg[0]; } return true; } #define print_buf(buf, ...) snprintf(buf, sizeof(buf), __VA_ARGS__) static void load_texture_data(GLuint obj, const struct texture_image *img, bool smooth, GLenum wrap) { glBindTexture(GL_TEXTURE_2D, obj); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap); 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); #ifndef HAVE_PSGL glPixelStorei(GL_UNPACK_ALIGNMENT, 4); #endif 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); } static bool load_textures(void) { unsigned i; if (!cg_shader->luts) return true; glGenTextures(cg_shader->luts, lut_textures); for (i = 0; i < cg_shader->luts; i++) { RARCH_LOG("Loading image from: \"%s\".\n", cg_shader->lut[i].path); struct texture_image img = {0}; if (!texture_image_load(cg_shader->lut[i].path, &img)) { RARCH_ERR("Failed to load picture ...\n"); return false; } load_texture_data(lut_textures[i], &img, cg_shader->lut[i].filter != RARCH_FILTER_NEAREST, gl_wrap_type_to_enum(cg_shader->lut[i].wrap)); texture_image_free(&img); } glBindTexture(GL_TEXTURE_2D, 0); return true; } static bool load_imports(void) { unsigned i; if (!cg_shader->variables) return true; struct state_tracker_info tracker_info = {0}; for (i = 0; i < cg_shader->variables; i++) { unsigned memtype; switch (cg_shader->variable[i].ram_type) { case RARCH_STATE_WRAM: memtype = RETRO_MEMORY_SYSTEM_RAM; break; default: memtype = -1u; } if ((memtype != -1u) && (cg_shader->variable[i].addr >= pretro_get_memory_size(memtype))) { RARCH_ERR("Address out of bounds.\n"); return false; } } tracker_info.wram = (uint8_t*)pretro_get_memory_data(RETRO_MEMORY_SYSTEM_RAM); tracker_info.info = cg_shader->variable; tracker_info.info_elem = cg_shader->variables; #ifdef HAVE_PYTHON if (*cg_shader->script_path) { tracker_info.script = cg_shader->script_path; tracker_info.script_is_file = true; } tracker_info.script_class = *cg_shader->script_class ? cg_shader->script_class : NULL; #endif state_tracker = state_tracker_init(&tracker_info); if (!state_tracker) RARCH_WARN("Failed to initialize state tracker.\n"); return true; } static bool load_shader(unsigned i) { RARCH_LOG("Loading Cg shader: \"%s\".\n", cg_shader->pass[i].source.cg); if (!load_program(i + 1, cg_shader->pass[i].source.cg, true)) return false; return true; } static bool load_preset(const char *path) { unsigned i; if (!load_stock()) return false; RARCH_LOG("Loading Cg meta-shader: %s\n", path); config_file_t *conf = config_file_new(path); if (!conf) { RARCH_ERR("Failed to load preset.\n"); return false; } if (!cg_shader) cg_shader = (struct gfx_shader*)calloc(1, sizeof(*cg_shader)); if (!cg_shader) return false; if (!gfx_shader_read_conf_cgp(conf, cg_shader)) { RARCH_ERR("Failed to parse CGP file.\n"); config_file_free(conf); return false; } config_file_free(conf); gfx_shader_resolve_relative(cg_shader, path); if (cg_shader->passes > GFX_MAX_SHADERS - 3) { RARCH_WARN("Too many shaders ... Capping shader amount to %d.\n", GFX_MAX_SHADERS - 3); cg_shader->passes = GFX_MAX_SHADERS - 3; } for (i = 0; i < cg_shader->passes; i++) { if (!load_shader(i)) { RARCH_ERR("Failed to load shaders ...\n"); return false; } } if (!load_textures()) { RARCH_ERR("Failed to load lookup textures ...\n"); return false; } if (!load_imports()) { RARCH_ERR("Failed to load imports ...\n"); return false; } return true; } static void set_program_base_attrib(unsigned i) { CGparameter param = cgGetFirstParameter(prg[i].vprg, CG_PROGRAM); for (; param; param = cgGetNextParameter(param)) { if (cgGetParameterDirection(param) != CG_IN || cgGetParameterVariability(param) != CG_VARYING) continue; const char *semantic = cgGetParameterSemantic(param); if (!semantic) continue; RARCH_LOG("CG: Found semantic \"%s\" in prog #%u.\n", semantic, i); if (strcmp(semantic, "TEXCOORD") == 0 || strcmp(semantic, "TEXCOORD0") == 0) prg[i].tex = param; else if (strcmp(semantic, "COLOR") == 0 || strcmp(semantic, "COLOR0") == 0) prg[i].color = param; else if (strcmp(semantic, "POSITION") == 0) prg[i].vertex = param; else if (strcmp(semantic, "TEXCOORD1") == 0) prg[i].lut_tex = param; } if (!prg[i].tex) prg[i].tex = cgGetNamedParameter(prg[i].vprg, "IN.tex_coord"); if (!prg[i].color) prg[i].color = cgGetNamedParameter(prg[i].vprg, "IN.color"); if (!prg[i].vertex) prg[i].vertex = cgGetNamedParameter(prg[i].vprg, "IN.vertex_coord"); if (!prg[i].lut_tex) prg[i].lut_tex = cgGetNamedParameter(prg[i].vprg, "IN.lut_tex_coord"); } static void set_pass_attrib(struct cg_program *prg, struct cg_fbo_params *fbo, const char *attr) { char attr_buf[64]; snprintf(attr_buf, sizeof(attr_buf), "%s.texture", attr); if (!fbo->tex) fbo->tex = cgGetNamedParameter(prg->fprg, attr_buf); snprintf(attr_buf, sizeof(attr_buf), "%s.video_size", attr); if (!fbo->vid_size_v) fbo->vid_size_v = cgGetNamedParameter(prg->vprg, attr_buf); if (!fbo->vid_size_f) fbo->vid_size_f = cgGetNamedParameter(prg->fprg, attr_buf); snprintf(attr_buf, sizeof(attr_buf), "%s.texture_size", attr); if (!fbo->tex_size_v) fbo->tex_size_v = cgGetNamedParameter(prg->vprg, attr_buf); if (!fbo->tex_size_f) fbo->tex_size_f = cgGetNamedParameter(prg->fprg, attr_buf); snprintf(attr_buf, sizeof(attr_buf), "%s.tex_coord", attr); if (!fbo->coord) fbo->coord = cgGetNamedParameter(prg->vprg, attr_buf); } static void set_program_attributes(unsigned i) { unsigned j; cgGLBindProgram(prg[i].fprg); cgGLBindProgram(prg[i].vprg); set_program_base_attrib(i); 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) prg[i].mvp = cgGetNamedParameter(prg[i].vprg, "IN.mvp_matrix"); 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"); if (i > 1) { char pass_str[64]; snprintf(pass_str, sizeof(pass_str), "PASSPREV%u", i); set_pass_attrib(&prg[i], &prg[i].orig, pass_str); } for (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 (j = 0; j < i - 1; j++) { char pass_str[64]; snprintf(pass_str, sizeof(pass_str), "PASS%u", j + 1); set_pass_attrib(&prg[i], &prg[i].fbo[j], pass_str); snprintf(pass_str, sizeof(pass_str), "PASSPREV%u", i - j); set_pass_attrib(&prg[i], &prg[i].fbo[j], pass_str); } } static bool gl_cg_init(const char *path) { unsigned i; #ifdef HAVE_CG_RUNTIME_COMPILER cgRTCgcInit(); #endif if (!cgCtx) cgCtx = cgCreateContext(); if (cgCtx == NULL) { RARCH_ERR("Failed to create Cg context\n"); return false; } #ifdef RARCH_CG_DEBUG cgGLSetDebugMode(CG_TRUE); cgSetErrorHandler(cg_error_handler, NULL); #endif cgFProf = cgGLGetLatestProfile(CG_GL_FRAGMENT); cgVProf = cgGLGetLatestProfile(CG_GL_VERTEX); if (cgFProf == CG_PROFILE_UNKNOWN || cgVProf == CG_PROFILE_UNKNOWN) { RARCH_ERR("Invalid profile type\n"); goto error; } #ifndef HAVE_RGL RARCH_LOG("[Cg]: Vertex profile: %s\n", cgGetProfileString(cgVProf)); RARCH_LOG("[Cg]: Fragment profile: %s\n", cgGetProfileString(cgFProf)); #endif cgGLSetOptimalOptions(cgFProf); cgGLSetOptimalOptions(cgVProf); cgGLEnableProfile(cgFProf); cgGLEnableProfile(cgVProf); if (path && strcmp(path_get_extension(path), "cgp") == 0) { if (!load_preset(path)) goto error; } else { if (!load_plain(path)) goto error; } prg[0].mvp = cgGetNamedParameter(prg[0].vprg, "IN.mvp_matrix"); for (i = 1; i <= cg_shader->passes; i++) set_program_attributes(i); // 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[cg_shader->passes + 1] = prg[0]; // No need to apply Android hack in Cg. prg[GL_SHADER_STOCK_BLEND] = prg[0]; cgGLBindProgram(prg[1].fprg); cgGLBindProgram(prg[1].vprg); cg_active = true; return true; error: gl_cg_deinit(); return false; } static void gl_cg_use(unsigned index) { if (cg_active && prg[index].vprg && prg[index].fprg) { gl_cg_reset_attrib(); active_index = index; cgGLBindProgram(prg[index].vprg); cgGLBindProgram(prg[index].fprg); } } static unsigned gl_cg_num(void) { if (cg_active) return cg_shader->passes; else return 0; } static bool gl_cg_filter_type(unsigned index, bool *smooth) { if (cg_active && index) { if (cg_shader->pass[index - 1].filter == RARCH_FILTER_UNSPEC) return false; *smooth = cg_shader->pass[index - 1].filter == RARCH_FILTER_LINEAR; return true; } else return false; } static enum gfx_wrap_type gl_cg_wrap_type(unsigned index) { if (cg_active && index) return cg_shader->pass[index - 1].wrap; else return RARCH_WRAP_BORDER; } static void gl_cg_shader_scale(unsigned index, struct gfx_fbo_scale *scale) { if (cg_active && index) *scale = cg_shader->pass[index - 1].fbo; else scale->valid = false; } static unsigned gl_cg_get_prev_textures(void) { unsigned i, j; if (!cg_active) return 0; unsigned max_prev = 0; for (i = 1; i <= cg_shader->passes; i++) for (j = 0; j < PREV_TEXTURES; j++) if (prg[i].prev[j].tex) max_prev = max(j + 1, max_prev); return max_prev; } void gl_cg_set_compiler_args(const char **argv) { cg_arguments = argv; } void gl_cg_invalidate_context(void) { cgCtx = NULL; } const gl_shader_backend_t gl_cg_backend = { gl_cg_init, gl_cg_deinit, gl_cg_set_params, gl_cg_use, gl_cg_num, gl_cg_filter_type, gl_cg_wrap_type, gl_cg_shader_scale, gl_cg_set_coords, gl_cg_set_mvp, gl_cg_get_prev_textures, RARCH_SHADER_CG, };