mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-07 08:36:02 -10:00
The statestream block index keys its blocks with xxh3_64(), against XXH32 a tenth of the time on a 16 KiB block and under half on 64 and 128 octets. The PS4 shader cache keeps its XXH64 keys, now from xxh64_update() on the stack, so cached binaries still match. Nothing else used deps/xxHash, which goes.
2170 lines
66 KiB
C
2170 lines
66 KiB
C
/* RetroArch - A frontend for libretro.
|
|
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
|
|
* Copyright (C) 2011-2017 - Daniel De Matteis
|
|
*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <compat/strl.h>
|
|
#include <compat/posix_string.h>
|
|
#include <string/stdstring.h>
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "../../config.h"
|
|
#endif
|
|
|
|
#if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES)
|
|
#include <gfx/gl_capabilities.h>
|
|
#include "../common/gl2_common.h"
|
|
#endif
|
|
|
|
#include "shader_glsl.h"
|
|
#include "../video_shader_parse.h"
|
|
#ifdef HAVE_REWIND
|
|
#include "../../state_manager.h"
|
|
#endif
|
|
#include "../../core.h"
|
|
#include "../../retroarch.h"
|
|
#include "../../verbosity.h"
|
|
#include "../../input/input_driver.h"
|
|
|
|
#if defined(ORBIS)
|
|
#include <encodings/xxh64.h>
|
|
#endif
|
|
|
|
#if defined(VITA)
|
|
#include <psp2/kernel/threadmgr/thread.h>
|
|
#include <psp2/power.h>
|
|
#endif
|
|
|
|
#define PREV_TEXTURES (GFX_MAX_TEXTURES - 1)
|
|
|
|
/* Cache the VBO. */
|
|
struct cache_vbo
|
|
{
|
|
GLuint vbo_primary;
|
|
GLuint vbo_secondary;
|
|
size_t len_primary;
|
|
size_t len_secondary;
|
|
GLfloat *buffer_primary;
|
|
GLfloat *buffer_secondary;
|
|
/* The static vertex array vbo_primary holds, for the ribbon
|
|
* pipelines: its address, length and first and last floats, so a
|
|
* rebuilt array at the same address is not taken for it. NULL when
|
|
* the buffer holds anything else. */
|
|
const GLfloat *static_src;
|
|
size_t static_vertices;
|
|
GLfloat static_head[4];
|
|
GLfloat static_tail[4];
|
|
};
|
|
|
|
struct shader_program_glsl_data
|
|
{
|
|
GLuint vprg;
|
|
GLuint fprg;
|
|
|
|
GLuint id;
|
|
};
|
|
|
|
struct glsl_attrib
|
|
{
|
|
GLint loc;
|
|
GLsizei size;
|
|
GLsizei offset;
|
|
};
|
|
|
|
struct shader_uniforms_frame
|
|
{
|
|
int texture;
|
|
int input_size;
|
|
int texture_size;
|
|
int tex_coord;
|
|
};
|
|
|
|
struct shader_uniforms
|
|
{
|
|
#if defined(VITA)
|
|
int time;
|
|
#endif
|
|
int mvp;
|
|
int tex_coord;
|
|
int vertex_coord;
|
|
int color;
|
|
int lut_tex_coord;
|
|
|
|
int input_size;
|
|
int output_size;
|
|
int texture_size;
|
|
int final_vp_size;
|
|
|
|
int frame_count;
|
|
int swap_count;
|
|
int frame_direction;
|
|
int frame_time_delta;
|
|
float original_fps;
|
|
/* Use int for maximal compatibility despite other drivers using uint. */
|
|
int rotation;
|
|
|
|
float core_aspect;
|
|
float core_aspect_rot;
|
|
|
|
int gyroscope;
|
|
int accelerometer;
|
|
int accelerometer_rest;
|
|
|
|
int lut_texture[GFX_MAX_TEXTURES];
|
|
unsigned frame_count_mod;
|
|
|
|
struct shader_uniforms_frame orig;
|
|
struct shader_uniforms_frame feedback;
|
|
struct shader_uniforms_frame pass[GFX_MAX_SHADERS];
|
|
struct shader_uniforms_frame prev[PREV_TEXTURES];
|
|
};
|
|
|
|
static const char *glsl_prefixes[] = {
|
|
"",
|
|
"ruby",
|
|
};
|
|
|
|
#if defined(VITA)
|
|
#include "../drivers/gl_shaders/modern_opaque.cg.vert.h"
|
|
#include "../drivers/gl_shaders/modern_opaque.cg.frag.h"
|
|
#include "../drivers/gl_shaders/modern_alpha_blend.cg.vert.h"
|
|
#include "../drivers/gl_shaders/modern_alpha_blend.cg.frag.h"
|
|
|
|
#ifdef HAVE_SHADERPIPELINE
|
|
#include "../drivers/gl_shaders/modern_pipeline_xmb_ribbon_simple.cg.vert.h"
|
|
#include "../drivers/gl_shaders/pipeline_xmb_ribbon_simple.cg.frag.h"
|
|
#include "../drivers/gl_shaders/modern_pipeline_xmb_ribbon.cg.vert.h"
|
|
#include "../drivers/gl_shaders/pipeline_xmb_ribbon.cg.frag.h"
|
|
#endif
|
|
|
|
#else
|
|
#include "../drivers/gl_shaders/modern_opaque.glsl.vert.h"
|
|
#include "../drivers/gl_shaders/modern_opaque.glsl.frag.h"
|
|
#include "../drivers/gl_shaders/core_opaque.glsl.vert.h"
|
|
#include "../drivers/gl_shaders/core_opaque.glsl.frag.h"
|
|
#include "../drivers/gl_shaders/legacy_opaque.glsl.vert.h"
|
|
#include "../drivers/gl_shaders/legacy_opaque.glsl.frag.h"
|
|
#include "../drivers/gl_shaders/modern_alpha_blend.glsl.vert.h"
|
|
#include "../drivers/gl_shaders/modern_alpha_blend.glsl.frag.h"
|
|
#include "../drivers/gl_shaders/core_alpha_blend.glsl.vert.h"
|
|
#include "../drivers/gl_shaders/core_alpha_blend.glsl.frag.h"
|
|
|
|
#ifdef HAVE_SHADERPIPELINE
|
|
#include "../drivers/gl_shaders/core_pipeline_snow.glsl.frag.h"
|
|
#include "../drivers/gl_shaders/core_pipeline_snow_simple.glsl.frag.h"
|
|
#include "../drivers/gl_shaders/core_pipeline_xmb_ribbon.glsl.frag.h"
|
|
#include "../drivers/gl_shaders/core_pipeline_xmb_ribbon_simple.glsl.frag.h"
|
|
#include "../drivers/gl_shaders/core_pipeline_bokeh.glsl.frag.h"
|
|
#include "../drivers/gl_shaders/core_pipeline_snowflake.glsl.frag.h"
|
|
#include "../drivers/gl_shaders/legacy_pipeline_xmb_ribbon_simple.glsl.vert.h"
|
|
#include "../drivers/gl_shaders/modern_pipeline_xmb_ribbon_simple.glsl.vert.h"
|
|
#include "../drivers/gl_shaders/pipeline_xmb_ribbon_simple.glsl.frag.h"
|
|
#include "../drivers/gl_shaders/pipeline_snow.glsl.frag.h"
|
|
#include "../drivers/gl_shaders/pipeline_snow.glsl.vert.h"
|
|
#include "../drivers/gl_shaders/pipeline_snow_core.glsl.vert.h"
|
|
#include "../drivers/gl_shaders/pipeline_snow_simple.glsl.frag.h"
|
|
#include "../drivers/gl_shaders/legacy_pipeline_snow.glsl.vert.h"
|
|
#include "../drivers/gl_shaders/legacy_pipeline_xmb_ribbon.glsl.vert.h"
|
|
#include "../drivers/gl_shaders/modern_pipeline_xmb_ribbon.glsl.vert.h"
|
|
#include "../drivers/gl_shaders/pipeline_xmb_ribbon.glsl.frag.h"
|
|
#include "../drivers/gl_shaders/pipeline_bokeh.glsl.frag.h"
|
|
#include "../drivers/gl_shaders/pipeline_snowflake.glsl.frag.h"
|
|
#endif
|
|
#endif
|
|
|
|
typedef struct glsl_shader_data
|
|
{
|
|
char alias_define[1024];
|
|
GLint attribs_elems[32 * PREV_TEXTURES + 2 + 4 + GFX_MAX_SHADERS];
|
|
unsigned attribs_index;
|
|
unsigned active_idx;
|
|
unsigned current_idx;
|
|
GLuint lut_textures[GFX_MAX_TEXTURES];
|
|
float current_mat_data[GFX_MAX_SHADERS];
|
|
float* current_mat_data_pointer[GFX_MAX_SHADERS];
|
|
struct shader_uniforms uniforms[GFX_MAX_SHADERS];
|
|
struct cache_vbo vbo[GFX_MAX_SHADERS];
|
|
struct shader_program_glsl_data prg[GFX_MAX_SHADERS];
|
|
struct video_shader *shader;
|
|
/* Staging for set_coords on draws of more than four vertices (font
|
|
* runs, the menu ribbon), grown on demand and kept for the shader's
|
|
* lifetime; it was malloc'd and freed on every such draw. */
|
|
GLfloat *coord_scratch;
|
|
size_t coord_scratch_cap;
|
|
/* gfx_display meshes: made the first time one is drawn, and not
|
|
* tried again once it could not be */
|
|
GLuint mesh_prg;
|
|
GLint mesh_mvp;
|
|
GLint mesh_tint;
|
|
GLint mesh_attr[3];
|
|
bool mesh_failed;
|
|
/* The menu effects already compiled or tried, one bit each, so one
|
|
* that will not compile is not tried again every frame */
|
|
uint8_t menu_shaders_tried;
|
|
} glsl_shader_data_t;
|
|
|
|
/* A gfx_display mesh vertex: three floats, then texture coordinates and
|
|
* colour as normalised integers the vertex fetch widens */
|
|
static const char *stock_vertex_mesh_legacy = GLSL(
|
|
attribute vec3 VertexCoord;
|
|
attribute vec2 TexCoord;
|
|
attribute vec4 Color;
|
|
uniform mat4 MVPMatrix;
|
|
uniform vec4 Tint;
|
|
varying vec2 tex_coord;
|
|
varying vec4 color;
|
|
|
|
void main() {
|
|
gl_Position = MVPMatrix * vec4(VertexCoord, 1.0);
|
|
tex_coord = TexCoord;
|
|
color = Color * Tint;
|
|
}
|
|
);
|
|
|
|
static const char *stock_fragment_mesh_legacy = GLSL(
|
|
uniform sampler2D Texture;
|
|
varying vec2 tex_coord;
|
|
varying vec4 color;
|
|
|
|
void main() {
|
|
gl_FragColor = color * texture2D(Texture, tex_coord);
|
|
}
|
|
);
|
|
|
|
static const char *stock_vertex_mesh_core = GLSL(
|
|
in vec3 VertexCoord;
|
|
in vec2 TexCoord;
|
|
in vec4 Color;
|
|
uniform mat4 MVPMatrix;
|
|
uniform vec4 Tint;
|
|
out vec2 tex_coord;
|
|
out vec4 color;
|
|
|
|
void main() {
|
|
gl_Position = MVPMatrix * vec4(VertexCoord, 1.0);
|
|
tex_coord = TexCoord;
|
|
color = Color * Tint;
|
|
}
|
|
);
|
|
|
|
static const char *stock_fragment_mesh_core = GLSL(
|
|
uniform sampler2D Texture;
|
|
in vec2 tex_coord;
|
|
in vec4 color;
|
|
out vec4 FragColor;
|
|
|
|
void main() {
|
|
FragColor = color * texture(Texture, tex_coord);
|
|
}
|
|
);
|
|
|
|
/* TODO/FIXME - static globals */
|
|
static bool glsl_core;
|
|
static unsigned glsl_major;
|
|
static unsigned glsl_minor;
|
|
|
|
static GLint gl_glsl_get_uniform(glsl_shader_data_t *glsl,
|
|
GLuint prog, const char *base)
|
|
{
|
|
unsigned i;
|
|
GLint loc;
|
|
char buf[80];
|
|
size_t _len = strlcpy(buf, glsl->shader->prefix, sizeof(buf));
|
|
strlcpy(buf + _len, base, sizeof(buf) - _len);
|
|
if ((loc = glGetUniformLocation(prog, buf)) >= 0)
|
|
return loc;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(glsl_prefixes); i++)
|
|
{
|
|
buf[0] = '\0';
|
|
_len = strlcpy(buf, glsl_prefixes[i], sizeof(buf));
|
|
strlcpy(buf + _len, base, sizeof(buf) - _len);
|
|
if ((loc = glGetUniformLocation(prog, buf)) >= 0)
|
|
return loc;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static GLint gl_glsl_get_attrib(glsl_shader_data_t *glsl,
|
|
GLuint prog, const char *base)
|
|
{
|
|
unsigned i;
|
|
GLint loc;
|
|
char buf[80];
|
|
size_t _len = strlcpy(buf, glsl->shader->prefix, sizeof(buf));
|
|
strlcpy(buf + _len, base, sizeof(buf) - _len);
|
|
if ((loc = glGetUniformLocation(prog, buf)) >= 0)
|
|
return loc;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(glsl_prefixes); i++)
|
|
{
|
|
_len = strlcpy(buf, glsl_prefixes[i], sizeof(buf));
|
|
strlcpy(buf + _len, base, sizeof(buf) - _len);
|
|
if ((loc = glGetAttribLocation(prog, buf)) >= 0)
|
|
return loc;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static void gl_glsl_print_shader_log(GLuint obj)
|
|
{
|
|
char *info_log = NULL;
|
|
GLint max_len, info_len = 0;
|
|
|
|
glGetShaderiv(obj, GL_INFO_LOG_LENGTH, &max_len);
|
|
|
|
if (max_len == 0)
|
|
return;
|
|
|
|
info_log = (char*)malloc(max_len);
|
|
if (!info_log)
|
|
return;
|
|
|
|
glGetShaderInfoLog(obj, max_len, &info_len, info_log);
|
|
|
|
if (info_len > 0)
|
|
RARCH_LOG("[GLSL] Shader log: %s.\n", info_log);
|
|
|
|
free(info_log);
|
|
}
|
|
|
|
static void gl_glsl_print_linker_log(GLuint obj)
|
|
{
|
|
char *info_log = NULL;
|
|
GLint max_len, info_len = 0;
|
|
|
|
glGetProgramiv(obj, GL_INFO_LOG_LENGTH, &max_len);
|
|
|
|
if (max_len == 0)
|
|
return;
|
|
|
|
info_log = (char*)malloc(max_len);
|
|
if (!info_log)
|
|
return;
|
|
|
|
glGetProgramInfoLog(obj, max_len, &info_len, info_log);
|
|
|
|
if (info_len > 0)
|
|
RARCH_LOG("[GLSL] Linker log: %s.\n", info_log);
|
|
|
|
free(info_log);
|
|
}
|
|
|
|
#if defined(ORBIS)
|
|
static uint64_t gl_glsl_hash_shader(
|
|
const char **source, const int source_length)
|
|
{
|
|
int n;
|
|
xxh64_state_t state;
|
|
|
|
xxh64_reset(&state, 0xAABBCCDDu);
|
|
for (n = 0; n < source_length; n++)
|
|
xxh64_update(&state, source[n], strlen(source[n]));
|
|
return xxh64_digest(&state);
|
|
}
|
|
|
|
/* The shader binary stored under @hash, if there is one */
|
|
static bool gl_glsl_load_binary_shader(GLuint shader, uint64_t hash)
|
|
{
|
|
GLint status;
|
|
char key[32];
|
|
video_shader_cache_view_t view;
|
|
|
|
snprintf(key, sizeof(key), "%lx", (unsigned long)hash);
|
|
if (!video_shader_cache_map(VIDEO_SHADER_CACHE_ORBIS_BINARY, key, &view))
|
|
return false;
|
|
if (view.len > 0x7fffffff)
|
|
{
|
|
video_shader_cache_unmap(&view);
|
|
return false;
|
|
}
|
|
glShaderBinary(1, &shader, 2, view.data, (GLsizei)view.len);
|
|
video_shader_cache_unmap(&view);
|
|
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
|
|
return status == GL_TRUE;
|
|
}
|
|
#endif
|
|
|
|
static bool gl_glsl_compile_shader(glsl_shader_data_t *glsl,
|
|
GLuint shader,
|
|
const char *define, const char *program)
|
|
{
|
|
GLint status;
|
|
const char *source[4];
|
|
char version[32];
|
|
const char *existing_version = strstr(program, "#version");
|
|
|
|
version[0] = '\0';
|
|
|
|
if (existing_version)
|
|
{
|
|
const char* version_extra = "";
|
|
unsigned version_no = (unsigned)
|
|
strtoul(existing_version + 8, (char**)&program, 10);
|
|
|
|
#ifdef HAVE_OPENGLES
|
|
if (gl_check_capability(GL_CAPS_GLES3_SUPPORTED))
|
|
{
|
|
if (version_no >= 130 && version_no < 330)
|
|
{
|
|
version_extra = " es";
|
|
version_no = 300;
|
|
}
|
|
else if (version_no == 330)
|
|
{
|
|
version_extra = " es";
|
|
version_no = 310;
|
|
}
|
|
else if (version_no > 330)
|
|
{
|
|
version_extra = " es";
|
|
version_no = 320;
|
|
}
|
|
else version_no = 100;
|
|
}
|
|
/* Avoid versions that definitely aren't supported. */
|
|
else version_no = 100;
|
|
#endif
|
|
snprintf(version,
|
|
sizeof(version), "#version %u%s\n", version_no, version_extra);
|
|
RARCH_LOG("[GLSL] Using GLSL version %u%s.\n", version_no, version_extra);
|
|
}
|
|
else if (glsl_core)
|
|
{
|
|
unsigned version_no = 0;
|
|
#ifdef HAVE_OPENGLES
|
|
/* glsl_core (the glcore/gl3 driver) uses modern in/out stock
|
|
* shaders. On GLES a desktop "#version 130" is rejected outright
|
|
* ("GLSL 1.30 is not supported"), which broke every GLSL shader on
|
|
* the glcore driver; emit an "... es" version instead, mirroring
|
|
* the user-shader mapping above. */
|
|
if (gl_check_capability(GL_CAPS_GLES3_SUPPORTED))
|
|
{
|
|
unsigned gl_ver = glsl_major * 100 + glsl_minor * 10;
|
|
if (gl_ver >= 320)
|
|
version_no = 320;
|
|
else if (gl_ver >= 310)
|
|
version_no = 310;
|
|
else
|
|
version_no = 300;
|
|
snprintf(version, sizeof(version), "#version %u es\n", version_no);
|
|
RARCH_LOG("[GLSL] Using GLSL version %u es.\n", version_no);
|
|
}
|
|
else
|
|
{
|
|
snprintf(version, sizeof(version), "#version 100\n");
|
|
RARCH_LOG("[GLSL] Using GLSL version 100.\n");
|
|
}
|
|
#else
|
|
unsigned gl_ver = glsl_major * 100 + glsl_minor * 10;
|
|
|
|
if (gl_ver >= 300)
|
|
version_no = 130;
|
|
else if (gl_ver >= 210)
|
|
version_no = 120;
|
|
else
|
|
version_no = 110;
|
|
|
|
snprintf(version, sizeof(version), "#version %u\n", version_no);
|
|
RARCH_LOG("[GLSL] Using GLSL version %u.\n", version_no);
|
|
#endif
|
|
}
|
|
|
|
source[0] = version;
|
|
source[1] = define;
|
|
source[2] = glsl->alias_define;
|
|
source[3] = program;
|
|
|
|
#if defined(ORBIS)
|
|
if (gl_glsl_load_binary_shader(shader,
|
|
gl_glsl_hash_shader(source, ARRAY_SIZE(source))))
|
|
return true;
|
|
#endif
|
|
|
|
glShaderSource(shader, ARRAY_SIZE(source), source, NULL);
|
|
glCompileShader(shader);
|
|
|
|
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
|
|
gl_glsl_print_shader_log(shader);
|
|
|
|
return status == GL_TRUE;
|
|
}
|
|
|
|
static bool gl_glsl_link_program(GLuint prog)
|
|
{
|
|
GLint status;
|
|
|
|
glLinkProgram(prog);
|
|
|
|
glGetProgramiv(prog, GL_LINK_STATUS, &status);
|
|
gl_glsl_print_linker_log(prog);
|
|
|
|
if (status != GL_TRUE)
|
|
return false;
|
|
|
|
glUseProgram(prog);
|
|
return true;
|
|
}
|
|
|
|
static bool gl_glsl_compile_program(
|
|
void *data,
|
|
unsigned idx,
|
|
void *program_data,
|
|
struct shader_program_info *program_info)
|
|
{
|
|
glsl_shader_data_t *glsl = (glsl_shader_data_t*)data;
|
|
struct shader_program_glsl_data *program =
|
|
(struct shader_program_glsl_data*)program_data;
|
|
GLuint prog = glCreateProgram();
|
|
|
|
if (!program)
|
|
program = &glsl->prg[idx];
|
|
|
|
if (!prog)
|
|
goto error;
|
|
|
|
if (program_info->vertex)
|
|
{
|
|
RARCH_LOG("[GLSL] Found GLSL vertex shader.\n");
|
|
program->vprg = glCreateShader(GL_VERTEX_SHADER);
|
|
|
|
if (!gl_glsl_compile_shader(
|
|
glsl,
|
|
program->vprg,
|
|
"#define VERTEX\n#define PARAMETER_UNIFORM\n#define _HAS_ORIGINALASPECT_UNIFORMS\n#define _HAS_FRAMETIME_UNIFORMS\n#define _HAS_SENSOR_UNIFORMS\n#define _HAS_SWAPCOUNT_UNIFORM\n",
|
|
program_info->vertex))
|
|
{
|
|
RARCH_ERR("[GLSL] Failed to compile vertex shader #%u.\n", idx);
|
|
goto error;
|
|
}
|
|
|
|
glAttachShader(prog, program->vprg);
|
|
}
|
|
|
|
if (program_info->fragment)
|
|
{
|
|
RARCH_LOG("[GLSL] Found GLSL fragment shader.\n");
|
|
program->fprg = glCreateShader(GL_FRAGMENT_SHADER);
|
|
if (!gl_glsl_compile_shader(glsl, program->fprg,
|
|
"#define FRAGMENT\n#define PARAMETER_UNIFORM\n#define _HAS_ORIGINALASPECT_UNIFORMS\n#define _HAS_FRAMETIME_UNIFORMS\n#define _HAS_SENSOR_UNIFORMS\n#define _HAS_SWAPCOUNT_UNIFORM\n",
|
|
program_info->fragment))
|
|
{
|
|
RARCH_ERR("[GLSL] Failed to compile fragment shader #%u.\n", idx);
|
|
goto error;
|
|
}
|
|
|
|
glAttachShader(prog, program->fprg);
|
|
}
|
|
|
|
if (program_info->vertex || program_info->fragment)
|
|
{
|
|
RARCH_LOG("[GLSL] Linking GLSL program.\n");
|
|
if (!gl_glsl_link_program(prog))
|
|
goto error;
|
|
|
|
/* Clean up dead memory. We're not going to relink the program.
|
|
* Detaching first seems to kill some mobile drivers
|
|
* (according to the intertubes anyways). */
|
|
if (program->vprg)
|
|
glDeleteShader(program->vprg);
|
|
if (program->fprg)
|
|
glDeleteShader(program->fprg);
|
|
program->vprg = 0;
|
|
program->fprg = 0;
|
|
|
|
glUseProgram(prog);
|
|
#if defined(VITA)
|
|
glUniform1i(gl_glsl_get_uniform(glsl, prog, "vTexture"), 0);
|
|
#else
|
|
glUniform1i(gl_glsl_get_uniform(glsl, prog, "Texture"), 0);
|
|
#endif
|
|
glUseProgram(0);
|
|
}
|
|
|
|
program->id = prog;
|
|
|
|
return true;
|
|
|
|
error:
|
|
RARCH_ERR("[GLSL] Failed to link program #%u.\n", idx);
|
|
program->id = 0;
|
|
return false;
|
|
}
|
|
|
|
static void gl_glsl_strip_parameter_pragmas(char *source, const char *str)
|
|
{
|
|
/* #pragma parameter lines tend to have " characters in them,
|
|
* which is not legal GLSL. */
|
|
char *s = strstr(source, str);
|
|
|
|
while (s)
|
|
{
|
|
/* #pragmas have to be on a single line,
|
|
* so we can just replace the entire line with spaces. */
|
|
while (*s != '\0' && *s != '\n')
|
|
*s++ = ' ';
|
|
s = strstr(s, str);
|
|
}
|
|
}
|
|
|
|
static bool gl_glsl_load_source_path(struct video_shader_pass *pass,
|
|
const char *path)
|
|
{
|
|
int64_t len = 0;
|
|
|
|
/* Asked for by name: this driver does not open it. What comes back
|
|
* is ours to free, as before. */
|
|
if ( !pass
|
|
|| !video_shader_source_read(path,
|
|
&pass->source.string.vertex, &len)
|
|
|| len <= 0)
|
|
return false;
|
|
|
|
gl_glsl_strip_parameter_pragmas(pass->source.string.vertex,
|
|
"#pragma parameter");
|
|
pass->source.string.fragment = strdup(pass->source.string.vertex);
|
|
return pass->source.string.fragment && pass->source.string.vertex;
|
|
}
|
|
|
|
static bool gl_glsl_compile_programs(
|
|
glsl_shader_data_t *glsl,
|
|
struct shader_program_glsl_data *program)
|
|
{
|
|
unsigned i;
|
|
|
|
for (i = 0; i < glsl->shader->passes; i++)
|
|
{
|
|
struct shader_program_info shader_prog_info;
|
|
const char *vertex = NULL;
|
|
const char *fragment = NULL;
|
|
struct video_shader_pass *pass = (struct video_shader_pass*)
|
|
&glsl->shader->pass[i];
|
|
|
|
if (!pass)
|
|
continue;
|
|
|
|
/* If we load from GLSLP (preset),
|
|
* load the file here.
|
|
*/
|
|
if ( *pass->source.path
|
|
&& !gl_glsl_load_source_path(pass, pass->source.path))
|
|
{
|
|
RARCH_ERR("[GLSL] Failed to load GLSL shader: %s.\n",
|
|
pass->source.path);
|
|
return false;
|
|
}
|
|
|
|
vertex = pass->source.string.vertex;
|
|
fragment = pass->source.string.fragment;
|
|
|
|
shader_prog_info.vertex = vertex;
|
|
shader_prog_info.fragment = fragment;
|
|
shader_prog_info.is_file = false;
|
|
|
|
if (!gl_glsl_compile_program(glsl, i,
|
|
&program[i],
|
|
&shader_prog_info))
|
|
{
|
|
RARCH_ERR("[GLSL] Failed to create GL program #%u.\n", i);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void gl_glsl_reset_attrib(glsl_shader_data_t *glsl)
|
|
{
|
|
unsigned i;
|
|
for (i = 0; i < glsl->attribs_index; i++)
|
|
glDisableVertexAttribArray(glsl->attribs_elems[i]);
|
|
glsl->attribs_index = 0;
|
|
}
|
|
|
|
/* Coords change from one draw to the next (each font flush, each menu
|
|
* quad), so the buffer is re-specified many times a frame: GL_STREAM_DRAW
|
|
* says so, and lets the driver keep it where the CPU writes it cheaply. */
|
|
static void gl_glsl_set_vbo(GLfloat **buffer, size_t *buffer_elems,
|
|
const GLfloat *data, size_t elems)
|
|
{
|
|
if (elems > *buffer_elems)
|
|
{
|
|
GLfloat *new_buffer = (GLfloat*)
|
|
realloc(*buffer, elems * sizeof(GLfloat));
|
|
|
|
/* The buffer is only a copy of what was last uploaded, so that an
|
|
* identical set of coords can skip the upload next time. Upload
|
|
* straight from the caller's data and leave the existing
|
|
* allocation - realloc keeps it on failure - marked empty, so the
|
|
* comparison in gl_glsl_set_attribs cannot read it and the next
|
|
* call retries the grow. */
|
|
if (!new_buffer)
|
|
{
|
|
glBufferData(GL_ARRAY_BUFFER, elems * sizeof(GLfloat),
|
|
data, GL_STREAM_DRAW);
|
|
*buffer_elems = 0;
|
|
return;
|
|
}
|
|
|
|
*buffer = new_buffer;
|
|
}
|
|
|
|
memcpy(*buffer, data, elems * sizeof(GLfloat));
|
|
glBufferData(GL_ARRAY_BUFFER, elems * sizeof(GLfloat),
|
|
data, GL_STREAM_DRAW);
|
|
*buffer_elems = elems;
|
|
}
|
|
|
|
static INLINE void gl_glsl_set_attribs(glsl_shader_data_t *glsl,
|
|
GLuint vbo,
|
|
GLfloat **buffer, size_t *buffer_elems,
|
|
const GLfloat *data, size_t elems,
|
|
const struct glsl_attrib *attrs, size_t num_attrs)
|
|
{
|
|
size_t i;
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
|
|
|
if (elems != *buffer_elems ||
|
|
memcmp(data, *buffer, elems * sizeof(GLfloat)))
|
|
gl_glsl_set_vbo(buffer, buffer_elems, data, elems);
|
|
|
|
for (i = 0; i < num_attrs; i++)
|
|
{
|
|
if (glsl->attribs_index < ARRAY_SIZE(glsl->attribs_elems))
|
|
{
|
|
GLint loc = attrs[i].loc;
|
|
|
|
glEnableVertexAttribArray(loc);
|
|
glVertexAttribPointer(loc, attrs[i].size, GL_FLOAT, GL_FALSE, 0,
|
|
(const GLvoid*)(uintptr_t)attrs[i].offset);
|
|
glsl->attribs_elems[glsl->attribs_index++] = loc;
|
|
}
|
|
else
|
|
RARCH_WARN("[GLSL] Attrib array buffer was overflown.\n");
|
|
}
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
}
|
|
|
|
static void gl_glsl_clear_uniforms_frame(
|
|
struct shader_uniforms_frame *frame)
|
|
{
|
|
frame->texture = -1;
|
|
frame->texture_size = -1;
|
|
frame->input_size = -1;
|
|
frame->tex_coord = -1;
|
|
}
|
|
|
|
static void gl_glsl_find_uniforms_frame(glsl_shader_data_t *glsl,
|
|
GLuint prog,
|
|
struct shader_uniforms_frame *frame, const char *base)
|
|
{
|
|
char uni[64];
|
|
size_t _len = strlcpy(uni, base, sizeof(uni));
|
|
|
|
if (frame->texture < 0)
|
|
{
|
|
strlcpy_lit(uni + _len, "Texture", sizeof(uni) - _len);
|
|
frame->texture = gl_glsl_get_uniform(glsl, prog, uni);
|
|
}
|
|
|
|
if (frame->tex_coord < 0)
|
|
{
|
|
strlcpy_lit(uni + _len, "TexCoord", sizeof(uni) - _len);
|
|
frame->tex_coord = gl_glsl_get_attrib(glsl, prog, uni);
|
|
}
|
|
|
|
if (frame->input_size < 0)
|
|
{
|
|
strlcpy_lit(uni + _len, "InputSize", sizeof(uni) - _len);
|
|
frame->input_size = gl_glsl_get_uniform(glsl, prog, uni);
|
|
}
|
|
|
|
if (frame->texture_size < 0)
|
|
{
|
|
strlcpy_lit(uni + _len, "TextureSize", sizeof(uni) - _len);
|
|
frame->texture_size = gl_glsl_get_uniform(glsl, prog, uni);
|
|
}
|
|
|
|
}
|
|
|
|
static void gl_glsl_find_uniforms(glsl_shader_data_t *glsl,
|
|
unsigned pass, GLuint prog,
|
|
struct shader_uniforms *uni)
|
|
{
|
|
unsigned i;
|
|
char frame_base[64];
|
|
|
|
frame_base[0] = '\0';
|
|
|
|
glUseProgram(prog);
|
|
|
|
#if defined(VITA)
|
|
uni->time = gl_glsl_get_uniform(glsl, prog, "Time");
|
|
#endif
|
|
|
|
uni->mvp = gl_glsl_get_uniform(glsl, prog, "MVPMatrix");
|
|
uni->tex_coord = gl_glsl_get_attrib(glsl, prog, "TexCoord");
|
|
uni->vertex_coord = gl_glsl_get_attrib(glsl, prog, "VertexCoord");
|
|
uni->color = gl_glsl_get_attrib(glsl, prog, "Color");
|
|
uni->lut_tex_coord = gl_glsl_get_attrib(glsl, prog, "LUTTexCoord");
|
|
|
|
uni->input_size = gl_glsl_get_uniform(glsl, prog, "InputSize");
|
|
uni->output_size = gl_glsl_get_uniform(glsl, prog, "OutputSize");
|
|
uni->texture_size = gl_glsl_get_uniform(glsl, prog, "TextureSize");
|
|
uni->final_vp_size = gl_glsl_get_uniform(glsl, prog, "FinalViewportSize");
|
|
|
|
uni->frame_count = gl_glsl_get_uniform(glsl, prog, "FrameCount");
|
|
uni->swap_count = gl_glsl_get_uniform(glsl, prog, "SwapCount");
|
|
uni->frame_direction = gl_glsl_get_uniform(glsl, prog, "FrameDirection");
|
|
uni->frame_time_delta = gl_glsl_get_uniform(glsl, prog, "FrameTimeDelta");
|
|
uni->original_fps = gl_glsl_get_uniform(glsl, prog, "OriginalFPS");
|
|
uni->rotation = gl_glsl_get_uniform(glsl, prog, "Rotation");
|
|
uni->core_aspect = gl_glsl_get_uniform(glsl, prog, "OriginalAspect");
|
|
uni->core_aspect_rot = gl_glsl_get_uniform(glsl, prog, "OriginalAspectRotated");
|
|
|
|
uni->gyroscope = gl_glsl_get_uniform(glsl, prog, "Gyroscope");
|
|
uni->accelerometer = gl_glsl_get_uniform(glsl, prog, "Accelerometer");
|
|
uni->accelerometer_rest = gl_glsl_get_uniform(glsl, prog, "AccelerometerRest");
|
|
|
|
if ( uni->gyroscope >= 0
|
|
|| uni->accelerometer >= 0
|
|
|| uni->accelerometer_rest >= 0)
|
|
input_driver_set_shader_uses_sensors(true);
|
|
|
|
for (i = 0; i < glsl->shader->luts; i++)
|
|
uni->lut_texture[i] = glGetUniformLocation(prog, glsl->shader->lut[i].id);
|
|
|
|
gl_glsl_clear_uniforms_frame(&uni->orig);
|
|
gl_glsl_find_uniforms_frame(glsl, prog, &uni->orig, "Orig");
|
|
gl_glsl_clear_uniforms_frame(&uni->feedback);
|
|
gl_glsl_find_uniforms_frame(glsl, prog, &uni->feedback, "Feedback");
|
|
|
|
if (pass > 1)
|
|
{
|
|
snprintf(frame_base, sizeof(frame_base), "PassPrev%u", pass);
|
|
gl_glsl_find_uniforms_frame(glsl, prog, &uni->orig, frame_base);
|
|
}
|
|
|
|
for (i = 0; i + 1 < pass; i++)
|
|
{
|
|
snprintf(frame_base, sizeof(frame_base), "Pass%u", i + 1);
|
|
gl_glsl_clear_uniforms_frame(&uni->pass[i]);
|
|
gl_glsl_find_uniforms_frame(glsl, prog, &uni->pass[i], frame_base);
|
|
snprintf(frame_base, sizeof(frame_base), "PassPrev%u", pass - (i + 1));
|
|
gl_glsl_find_uniforms_frame(glsl, prog, &uni->pass[i], frame_base);
|
|
|
|
if (*glsl->shader->pass[i].alias)
|
|
gl_glsl_find_uniforms_frame(glsl, prog, &uni->pass[i], glsl->shader->pass[i].alias);
|
|
}
|
|
|
|
gl_glsl_clear_uniforms_frame(&uni->prev[0]);
|
|
gl_glsl_find_uniforms_frame(glsl, prog, &uni->prev[0], "Prev");
|
|
for (i = 1; i < PREV_TEXTURES; i++)
|
|
{
|
|
snprintf(frame_base, sizeof(frame_base), "Prev%u", i);
|
|
gl_glsl_clear_uniforms_frame(&uni->prev[i]);
|
|
gl_glsl_find_uniforms_frame(glsl, prog, &uni->prev[i], frame_base);
|
|
}
|
|
|
|
glUseProgram(0);
|
|
}
|
|
|
|
static void gl_glsl_deinit_shader(glsl_shader_data_t *glsl)
|
|
{
|
|
unsigned i;
|
|
|
|
if (!glsl || !glsl->shader)
|
|
return;
|
|
|
|
for (i = 0; i < glsl->shader->passes; i++)
|
|
{
|
|
free(glsl->shader->pass[i].source.string.vertex);
|
|
free(glsl->shader->pass[i].source.string.fragment);
|
|
}
|
|
|
|
free(glsl->shader);
|
|
glsl->shader = NULL;
|
|
}
|
|
|
|
static void gl_glsl_destroy_resources(glsl_shader_data_t *glsl)
|
|
{
|
|
unsigned i;
|
|
|
|
if (!glsl)
|
|
return;
|
|
|
|
glsl->current_idx = 0;
|
|
|
|
glUseProgram(0);
|
|
|
|
if (glsl->mesh_prg)
|
|
glDeleteProgram(glsl->mesh_prg);
|
|
glsl->mesh_prg = 0;
|
|
glsl->mesh_failed = false;
|
|
|
|
for (i = 0; i < GFX_MAX_SHADERS; i++)
|
|
{
|
|
if (glsl->prg[i].id == 0 || (i && glsl->prg[i].id == glsl->prg[0].id))
|
|
continue;
|
|
if (!glIsProgram(glsl->prg[i].id))
|
|
continue;
|
|
|
|
glDeleteProgram(glsl->prg[i].id);
|
|
glsl->prg[i].id = 0;
|
|
}
|
|
|
|
if (glsl->shader && glsl->shader->luts)
|
|
glDeleteTextures(glsl->shader->luts, glsl->lut_textures);
|
|
|
|
memset(glsl->prg, 0, sizeof(glsl->prg));
|
|
memset(glsl->uniforms, 0, sizeof(glsl->uniforms));
|
|
glsl->active_idx = 0;
|
|
glsl->menu_shaders_tried = 0;
|
|
|
|
gl_glsl_deinit_shader(glsl);
|
|
|
|
gl_glsl_reset_attrib(glsl);
|
|
|
|
for (i = 0; i < GFX_MAX_SHADERS; i++)
|
|
{
|
|
if (glsl->vbo[i].vbo_primary)
|
|
glDeleteBuffers(1, &glsl->vbo[i].vbo_primary);
|
|
if (glsl->vbo[i].vbo_secondary)
|
|
glDeleteBuffers(1, &glsl->vbo[i].vbo_secondary);
|
|
|
|
free(glsl->vbo[i].buffer_primary);
|
|
free(glsl->vbo[i].buffer_secondary);
|
|
}
|
|
memset(&glsl->vbo, 0, sizeof(glsl->vbo));
|
|
}
|
|
|
|
static void gl_glsl_deinit(void *data)
|
|
{
|
|
glsl_shader_data_t *glsl = (glsl_shader_data_t*)data;
|
|
|
|
if (!glsl)
|
|
return;
|
|
|
|
gl_glsl_destroy_resources(glsl);
|
|
input_driver_set_shader_uses_sensors(false);
|
|
|
|
free(glsl->coord_scratch);
|
|
free(glsl);
|
|
}
|
|
|
|
/* One menu effect's program, made the first time the effect is used
|
|
* rather than all six at start: at most one is ever on screen */
|
|
static void gl_glsl_compile_menu_shader(glsl_shader_data_t *glsl, unsigned idx)
|
|
{
|
|
#ifdef HAVE_SHADERPIPELINE
|
|
struct shader_program_info shader_prog_info;
|
|
|
|
if (!glsl)
|
|
return;
|
|
shader_prog_info.is_file = false;
|
|
|
|
if (idx == VIDEO_SHADER_MENU)
|
|
{
|
|
#ifdef HAVE_OPENGLES
|
|
#if defined(VITA)
|
|
shader_prog_info.vertex = stock_vertex_xmb_ribbon_modern;
|
|
shader_prog_info.fragment = stock_fragment_xmb;
|
|
#else
|
|
if (gl_check_capability(GL_CAPS_GLES3_SUPPORTED))
|
|
{
|
|
shader_prog_info.vertex = stock_vertex_xmb_ribbon_modern;
|
|
shader_prog_info.fragment = core_stock_fragment_xmb;
|
|
}
|
|
else if (gl_query_extension("GL_OES_standard_derivatives"))
|
|
{
|
|
shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_ribbon_modern : stock_vertex_xmb_ribbon_legacy;
|
|
shader_prog_info.fragment = glsl_core ? core_stock_fragment_xmb : stock_fragment_xmb;
|
|
}
|
|
else
|
|
{
|
|
shader_prog_info.vertex = stock_vertex_xmb_ribbon_simple_legacy;
|
|
shader_prog_info.fragment = stock_fragment_xmb_ribbon_simple;
|
|
}
|
|
#endif
|
|
#else
|
|
shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_ribbon_modern : stock_vertex_xmb_ribbon_legacy;
|
|
shader_prog_info.fragment = glsl_core ? core_stock_fragment_xmb : stock_fragment_xmb;
|
|
#endif
|
|
|
|
RARCH_LOG("[GLSL] Compiling ribbon shader...\n");
|
|
gl_glsl_compile_program(
|
|
glsl,
|
|
VIDEO_SHADER_MENU,
|
|
&glsl->prg[VIDEO_SHADER_MENU],
|
|
&shader_prog_info);
|
|
gl_glsl_find_uniforms(glsl, 0, glsl->prg[VIDEO_SHADER_MENU].id,
|
|
&glsl->uniforms[VIDEO_SHADER_MENU]);
|
|
}
|
|
|
|
if (idx == VIDEO_SHADER_MENU_2)
|
|
{
|
|
#if defined(VITA)
|
|
shader_prog_info.vertex = stock_vertex_xmb_simple_modern;
|
|
shader_prog_info.fragment = stock_fragment_xmb_ribbon_simple;
|
|
#else
|
|
shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_simple_modern : stock_vertex_xmb_ribbon_simple_legacy;
|
|
shader_prog_info.fragment = glsl_core ? stock_fragment_xmb_ribbon_simple_core : stock_fragment_xmb_ribbon_simple;
|
|
#endif
|
|
|
|
RARCH_LOG("[GLSL] Compiling simple ribbon shader...\n");
|
|
gl_glsl_compile_program(
|
|
glsl,
|
|
VIDEO_SHADER_MENU_2,
|
|
&glsl->prg[VIDEO_SHADER_MENU_2],
|
|
&shader_prog_info);
|
|
gl_glsl_find_uniforms(glsl, 0, glsl->prg[VIDEO_SHADER_MENU_2].id,
|
|
&glsl->uniforms[VIDEO_SHADER_MENU_2]);
|
|
}
|
|
|
|
#if !defined(VITA)
|
|
if (idx == VIDEO_SHADER_MENU_3)
|
|
{
|
|
#if defined(HAVE_OPENGLES)
|
|
shader_prog_info.vertex = stock_vertex_xmb_snow;
|
|
shader_prog_info.fragment = stock_fragment_xmb_simple_snow;
|
|
#else
|
|
shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_snow_core : stock_vertex_xmb_snow_legacy;
|
|
shader_prog_info.fragment = glsl_core ? stock_fragment_xmb_simple_snow_core : stock_fragment_xmb_simple_snow;
|
|
#endif
|
|
|
|
RARCH_LOG("[GLSL] Compiling snow shader...\n");
|
|
gl_glsl_compile_program(
|
|
glsl,
|
|
VIDEO_SHADER_MENU_3,
|
|
&glsl->prg[VIDEO_SHADER_MENU_3],
|
|
&shader_prog_info);
|
|
gl_glsl_find_uniforms(glsl, 0, glsl->prg[VIDEO_SHADER_MENU_3].id,
|
|
&glsl->uniforms[VIDEO_SHADER_MENU_3]);
|
|
}
|
|
|
|
if (idx == VIDEO_SHADER_MENU_4)
|
|
{
|
|
#if defined(HAVE_OPENGLES)
|
|
shader_prog_info.vertex = stock_vertex_xmb_snow;
|
|
shader_prog_info.fragment = stock_fragment_xmb_snow;
|
|
#else
|
|
shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_snow_core : stock_vertex_xmb_snow_legacy;
|
|
shader_prog_info.fragment = glsl_core ? stock_fragment_xmb_snow_core : stock_fragment_xmb_snow;
|
|
#endif
|
|
|
|
RARCH_LOG("[GLSL] Compiling modern snow shader...\n");
|
|
gl_glsl_compile_program(
|
|
glsl,
|
|
VIDEO_SHADER_MENU_4,
|
|
&glsl->prg[VIDEO_SHADER_MENU_4],
|
|
&shader_prog_info);
|
|
gl_glsl_find_uniforms(glsl, 0, glsl->prg[VIDEO_SHADER_MENU_4].id,
|
|
&glsl->uniforms[VIDEO_SHADER_MENU_4]);
|
|
}
|
|
|
|
if (idx == VIDEO_SHADER_MENU_5)
|
|
{
|
|
#if defined(HAVE_OPENGLES)
|
|
shader_prog_info.vertex = stock_vertex_xmb_snow;
|
|
shader_prog_info.fragment = stock_fragment_xmb_bokeh;
|
|
#else
|
|
shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_snow_core : stock_vertex_xmb_snow_legacy;
|
|
shader_prog_info.fragment = glsl_core ? stock_fragment_xmb_bokeh_core : stock_fragment_xmb_bokeh;
|
|
#endif
|
|
|
|
RARCH_LOG("[GLSL] Compiling bokeh shader...\n");
|
|
gl_glsl_compile_program(
|
|
glsl,
|
|
VIDEO_SHADER_MENU_5,
|
|
&glsl->prg[VIDEO_SHADER_MENU_5],
|
|
&shader_prog_info);
|
|
gl_glsl_find_uniforms(glsl, 0, glsl->prg[VIDEO_SHADER_MENU_5].id,
|
|
&glsl->uniforms[VIDEO_SHADER_MENU_5]);
|
|
}
|
|
|
|
if (idx == VIDEO_SHADER_MENU_6)
|
|
{
|
|
#if defined(HAVE_OPENGLES)
|
|
shader_prog_info.vertex = stock_vertex_xmb_snow;
|
|
shader_prog_info.fragment = stock_fragment_xmb_snowflake;
|
|
#else
|
|
shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_snow_core : stock_vertex_xmb_snow_legacy;
|
|
shader_prog_info.fragment = glsl_core ? stock_fragment_xmb_snowflake_core : stock_fragment_xmb_snowflake;
|
|
#endif
|
|
|
|
RARCH_LOG("[GLSL] Compiling snowflake shader...\n");
|
|
gl_glsl_compile_program(
|
|
glsl,
|
|
VIDEO_SHADER_MENU_6,
|
|
&glsl->prg[VIDEO_SHADER_MENU_6],
|
|
&shader_prog_info);
|
|
gl_glsl_find_uniforms(glsl, 0, glsl->prg[VIDEO_SHADER_MENU_6].id,
|
|
&glsl->uniforms[VIDEO_SHADER_MENU_6]);
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
static void gl_glsl_init_menu_shaders(void *data)
|
|
{
|
|
/* The menu effects are compiled when first used */
|
|
(void)data;
|
|
}
|
|
|
|
static void *gl_glsl_init(void *data, const char *path)
|
|
{
|
|
unsigned i;
|
|
struct shader_program_info shader_prog_info;
|
|
bool shader_support = false;
|
|
#ifdef GLSL_DEBUG
|
|
char *err_string = NULL;
|
|
#endif
|
|
const char *stock_vertex = NULL;
|
|
const char *stock_fragment = NULL;
|
|
glsl_shader_data_t *glsl = (glsl_shader_data_t*)
|
|
calloc(1, sizeof(glsl_shader_data_t));
|
|
|
|
if (!glsl)
|
|
return NULL;
|
|
|
|
(void)shader_support;
|
|
|
|
#ifndef HAVE_OPENGLES
|
|
RARCH_LOG("[GLSL] Checking GLSL shader support...\n");
|
|
shader_support = glCreateProgram && glUseProgram && glCreateShader
|
|
&& glDeleteShader && glShaderSource && glCompileShader && glAttachShader
|
|
&& glDetachShader && glLinkProgram && glGetUniformLocation
|
|
&& glUniform1i && glUniform1f && glUniform2fv && glUniform4fv
|
|
&& glUniformMatrix4fv
|
|
&& glGetShaderiv && glGetShaderInfoLog && glGetProgramiv
|
|
&& glGetProgramInfoLog
|
|
&& glDeleteProgram && glGetAttachedShaders
|
|
&& glGetAttribLocation && glEnableVertexAttribArray
|
|
&& glDisableVertexAttribArray
|
|
&& glVertexAttribPointer
|
|
&& glGenBuffers && glBufferData && glDeleteBuffers && glBindBuffer;
|
|
|
|
if (!shader_support)
|
|
{
|
|
RARCH_ERR("[GLSL] GLSL shaders aren't supported by your OpenGL driver.\n");
|
|
goto error;
|
|
}
|
|
#endif
|
|
|
|
glsl->shader = (struct video_shader*)calloc(1, sizeof(*glsl->shader));
|
|
if (!glsl->shader)
|
|
goto error;
|
|
|
|
{
|
|
bool is_preset;
|
|
enum rarch_shader_type type =
|
|
video_shader_get_type_from_ext(path_get_extension(path), &is_preset);
|
|
|
|
if (path && *path && type != RARCH_SHADER_GLSL)
|
|
{
|
|
RARCH_ERR("[GLSL] Invalid shader type, falling back to stock.\n");
|
|
path = NULL;
|
|
}
|
|
|
|
if (path && *path)
|
|
{
|
|
bool ret = false;
|
|
|
|
if (is_preset)
|
|
{
|
|
ret = video_shader_load_preset_into_shader(path, glsl->shader);
|
|
glsl->shader->flags |= SHDR_FLAG_MODERN;
|
|
}
|
|
else
|
|
{
|
|
strlcpy(glsl->shader->pass[0].source.path, path,
|
|
sizeof(glsl->shader->pass[0].source.path));
|
|
glsl->shader->passes = 1;
|
|
glsl->shader->flags |= SHDR_FLAG_MODERN;
|
|
ret = true;
|
|
}
|
|
|
|
if (!ret)
|
|
{
|
|
RARCH_ERR("[GLSL] Failed to parse GLSL shader.\n");
|
|
goto error;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RARCH_WARN("[GLSL] Stock GLSL shaders will be used.\n");
|
|
glsl->shader->passes = 1;
|
|
#if defined(VITA)
|
|
glsl->shader->pass[0].source.string.vertex = strdup(stock_vertex_modern);
|
|
glsl->shader->pass[0].source.string.fragment = strdup(stock_fragment_modern);
|
|
#else
|
|
glsl->shader->pass[0].source.string.vertex =
|
|
strdup(glsl_core ? stock_vertex_core : stock_vertex_modern);
|
|
glsl->shader->pass[0].source.string.fragment =
|
|
strdup(glsl_core ? stock_fragment_core : stock_fragment_modern);
|
|
#endif
|
|
glsl->shader->flags |= SHDR_FLAG_MODERN;
|
|
}
|
|
}
|
|
|
|
#if defined(VITA)
|
|
stock_vertex = stock_vertex_modern;
|
|
stock_fragment = stock_fragment_modern;
|
|
#else
|
|
stock_vertex = (glsl->shader->flags & SHDR_FLAG_MODERN) ?
|
|
stock_vertex_modern : stock_vertex_legacy;
|
|
stock_fragment = (glsl->shader->flags & SHDR_FLAG_MODERN) ?
|
|
stock_fragment_modern : stock_fragment_legacy;
|
|
|
|
if (glsl_core)
|
|
{
|
|
stock_vertex = stock_vertex_core;
|
|
stock_fragment = stock_fragment_core;
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_OPENGLES
|
|
if (!(glsl->shader->flags & SHDR_FLAG_MODERN))
|
|
{
|
|
RARCH_ERR("[GLSL] GLES context is used, but shader is not modern. Cannot use it.\n");
|
|
goto error;
|
|
}
|
|
#else
|
|
if ( glsl_core
|
|
&& (!(glsl->shader->flags & SHDR_FLAG_MODERN)))
|
|
{
|
|
RARCH_ERR("[GLSL] GL core context is used, but shader is not core compatible. Cannot use it.\n");
|
|
goto error;
|
|
}
|
|
#endif
|
|
|
|
/* Find all aliases we use in our GLSLP and add #defines for them so
|
|
* that a shader can choose a fallback if we are not using a preset.
|
|
*
|
|
* Track a running offset into alias_define and let snprintf write
|
|
* directly at that offset; the prior strlcat-in-loop form scanned
|
|
* the buffer from the start on every iteration to find its end,
|
|
* giving O(passes^2) total cost. */
|
|
{
|
|
size_t alias_len = 0;
|
|
size_t alias_avail = sizeof(glsl->alias_define);
|
|
glsl->alias_define[0] = '\0';
|
|
for (i = 0; i < glsl->shader->passes; i++)
|
|
{
|
|
int n;
|
|
if (!*glsl->shader->pass[i].alias)
|
|
continue;
|
|
if (alias_len + 1 >= alias_avail)
|
|
break;
|
|
n = snprintf(glsl->alias_define + alias_len,
|
|
alias_avail - alias_len,
|
|
"#define %s_ALIAS\n",
|
|
glsl->shader->pass[i].alias);
|
|
if (n < 0 || (size_t)n >= alias_avail - alias_len)
|
|
break;
|
|
alias_len += (size_t)n;
|
|
}
|
|
}
|
|
|
|
shader_prog_info.vertex = stock_vertex;
|
|
shader_prog_info.fragment = stock_fragment;
|
|
shader_prog_info.is_file = false;
|
|
|
|
if (!gl_glsl_compile_program(glsl, 0, &glsl->prg[0], &shader_prog_info))
|
|
{
|
|
RARCH_ERR("[GLSL] GLSL stock programs failed to compile.\n");
|
|
goto error;
|
|
}
|
|
|
|
if (!gl_glsl_compile_programs(glsl, &glsl->prg[1]))
|
|
goto error;
|
|
|
|
if (!gl2_load_luts(glsl->shader, glsl->lut_textures))
|
|
{
|
|
RARCH_ERR("[GLSL] Failed to load LUTs.\n");
|
|
goto error;
|
|
}
|
|
|
|
for (i = 0; i <= glsl->shader->passes; i++)
|
|
gl_glsl_find_uniforms(glsl, i, glsl->prg[i].id, &glsl->uniforms[i]);
|
|
|
|
#ifdef GLSL_DEBUG
|
|
if (!gl_check_error(&err_string))
|
|
{
|
|
RARCH_ERR("%s\n", err_string);
|
|
free(err_string);
|
|
RARCH_WARN("[GLSL] Detected GL error in GLSL.\n");
|
|
}
|
|
#endif
|
|
|
|
glsl->prg[glsl->shader->passes + 1] = glsl->prg[0];
|
|
glsl->uniforms[glsl->shader->passes + 1] = glsl->uniforms[0];
|
|
|
|
if (glsl->shader->flags & SHDR_FLAG_MODERN)
|
|
{
|
|
#if defined(VITA)
|
|
shader_prog_info.vertex = stock_vertex_modern_blend;
|
|
shader_prog_info.fragment = stock_fragment_modern_blend;
|
|
#else
|
|
shader_prog_info.vertex =
|
|
glsl_core ?
|
|
stock_vertex_core_blend : stock_vertex_modern_blend;
|
|
shader_prog_info.fragment =
|
|
glsl_core ?
|
|
stock_fragment_core_blend : stock_fragment_modern_blend;
|
|
#endif
|
|
shader_prog_info.is_file = false;
|
|
|
|
gl_glsl_compile_program(
|
|
glsl,
|
|
VIDEO_SHADER_STOCK_BLEND,
|
|
&glsl->prg[VIDEO_SHADER_STOCK_BLEND],
|
|
&shader_prog_info
|
|
);
|
|
|
|
gl_glsl_find_uniforms(glsl, 0, glsl->prg[VIDEO_SHADER_STOCK_BLEND].id,
|
|
&glsl->uniforms[VIDEO_SHADER_STOCK_BLEND]);
|
|
}
|
|
else
|
|
{
|
|
glsl->prg[VIDEO_SHADER_STOCK_BLEND] = glsl->prg[0];
|
|
glsl->uniforms[VIDEO_SHADER_STOCK_BLEND] = glsl->uniforms[0];
|
|
}
|
|
|
|
gl_glsl_reset_attrib(glsl);
|
|
|
|
for (i = 0; i < GFX_MAX_SHADERS; i++)
|
|
{
|
|
glGenBuffers(1, &glsl->vbo[i].vbo_primary);
|
|
glGenBuffers(1, &glsl->vbo[i].vbo_secondary);
|
|
}
|
|
|
|
return glsl;
|
|
|
|
error:
|
|
gl_glsl_destroy_resources(glsl);
|
|
|
|
if (glsl)
|
|
free(glsl);
|
|
|
|
{
|
|
size_t _len;
|
|
char msg[NAME_MAX_LENGTH];
|
|
|
|
_len = snprintf(msg, sizeof(msg), "Failed to compile shader: \"%s\".",
|
|
path_basename(path));
|
|
|
|
runloop_msg_queue_push(msg, _len, 1, 120, true, NULL,
|
|
MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_ERROR);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void gl_glsl_set_uniform_parameter(
|
|
void *data,
|
|
struct uniform_info *param,
|
|
void *uniform_data)
|
|
{
|
|
GLint location = 0;
|
|
glsl_shader_data_t *glsl = (glsl_shader_data_t*)data;
|
|
|
|
if (!glsl || !param)
|
|
return;
|
|
|
|
if (param->lookup.enable)
|
|
location = glGetUniformLocation(glsl->prg[param->lookup.idx].id, param->lookup.ident);
|
|
else
|
|
location = param->location;
|
|
|
|
switch (param->type)
|
|
{
|
|
case UNIFORM_1F:
|
|
glUniform1f(location, param->result.f.v0);
|
|
break;
|
|
case UNIFORM_2F:
|
|
glUniform2f(location, param->result.f.v0,
|
|
param->result.f.v1);
|
|
break;
|
|
case UNIFORM_3F:
|
|
glUniform3f(location, param->result.f.v0,
|
|
param->result.f.v1, param->result.f.v2);
|
|
break;
|
|
case UNIFORM_4F:
|
|
glUniform4f(location, param->result.f.v0,
|
|
param->result.f.v1, param->result.f.v2,
|
|
param->result.f.v3);
|
|
break;
|
|
case UNIFORM_1FV:
|
|
glUniform1fv(location, 1, param->result.floatv);
|
|
break;
|
|
case UNIFORM_2FV:
|
|
glUniform2fv(location, 1, param->result.floatv);
|
|
break;
|
|
case UNIFORM_3FV:
|
|
glUniform3fv(location, 1, param->result.floatv);
|
|
break;
|
|
case UNIFORM_4FV:
|
|
glUniform4fv(location, 1, param->result.floatv);
|
|
break;
|
|
case UNIFORM_1I:
|
|
glUniform1i(location, (GLint)param->result.integer.v0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void gl_glsl_set_params(void *dat, void *shader_data)
|
|
{
|
|
unsigned i;
|
|
GLfloat buffer[512];
|
|
struct glsl_attrib attribs[32];
|
|
float input_size[2], output_size[2], texture_size[2], final_vp_size[2];
|
|
video_shader_ctx_params_t *params = (video_shader_ctx_params_t*)dat;
|
|
unsigned vp_width = VIDEO_SCALE_W(params->vp_dims);
|
|
unsigned vp_height = VIDEO_SCALE_H(params->vp_dims);
|
|
unsigned width = VIDEO_SCALE_W(params->dims);
|
|
unsigned height = VIDEO_SCALE_H(params->dims);
|
|
unsigned tex_width = VIDEO_SCALE_W(params->tex_dims);
|
|
unsigned tex_height = VIDEO_SCALE_H(params->tex_dims);
|
|
unsigned out_width = VIDEO_SCALE_W(params->out_dims);
|
|
unsigned out_height = VIDEO_SCALE_H(params->out_dims);
|
|
unsigned frame_count = params->frame_counter;
|
|
const void *_info = params->info;
|
|
const void *_prev_info = params->prev_info;
|
|
const void *_feedback_info = params->feedback_info;
|
|
const void *_fbo_info = params->fbo_info;
|
|
unsigned fbo_info_cnt = params->fbo_info_cnt;
|
|
unsigned texunit = 1;
|
|
const struct shader_uniforms *uni = NULL;
|
|
size_t size = 0;
|
|
size_t attribs_size = 0;
|
|
const struct video_tex_info *info = (const struct video_tex_info*)_info;
|
|
const struct video_tex_info *prev_info = (const struct video_tex_info*)_prev_info;
|
|
const struct video_tex_info *feedback_info = (const struct video_tex_info*)_feedback_info;
|
|
const struct video_tex_info *fbo_info = (const struct video_tex_info*)_fbo_info;
|
|
struct glsl_attrib *attr = (struct glsl_attrib*)attribs;
|
|
glsl_shader_data_t *glsl = (glsl_shader_data_t*)shader_data;
|
|
|
|
if (!glsl)
|
|
return;
|
|
|
|
uni = (const struct shader_uniforms*)&glsl->uniforms[glsl->active_idx];
|
|
|
|
if (glsl->prg[glsl->active_idx].id == 0)
|
|
return;
|
|
|
|
input_size [0] = (float)width;
|
|
input_size [1] = (float)height;
|
|
output_size[0] = (float)out_width;
|
|
output_size[1] = (float)out_height;
|
|
texture_size[0] = (float)tex_width;
|
|
texture_size[1] = (float)tex_height;
|
|
final_vp_size[0] = (float)vp_width;
|
|
final_vp_size[1] = (float)vp_height;
|
|
|
|
if (uni->input_size >= 0)
|
|
glUniform2fv(uni->input_size, 1, input_size);
|
|
|
|
if (uni->output_size >= 0)
|
|
glUniform2fv(uni->output_size, 1, output_size);
|
|
|
|
if (uni->texture_size >= 0)
|
|
glUniform2fv(uni->texture_size, 1, texture_size);
|
|
|
|
if (uni->final_vp_size >= 0)
|
|
glUniform2fv(uni->final_vp_size, 1, final_vp_size);
|
|
|
|
if (uni->frame_count >= 0 && glsl->active_idx)
|
|
{
|
|
unsigned modulo = glsl->shader->pass[glsl->active_idx - 1].frame_count_mod;
|
|
|
|
if (modulo)
|
|
frame_count %= modulo;
|
|
|
|
glUniform1i(uni->frame_count, frame_count);
|
|
}
|
|
|
|
/* Not modulo'd: SwapCount counts what the display was shown, so a
|
|
* pass's frame_count_mod has nothing to say about it. */
|
|
if (uni->swap_count >= 0)
|
|
glUniform1i(uni->swap_count, (int)params->swap_counter);
|
|
|
|
if (uni->frame_direction >= 0)
|
|
{
|
|
#ifdef HAVE_REWIND
|
|
if (state_manager_frame_is_reversed())
|
|
glUniform1i(uni->frame_direction, -1);
|
|
else
|
|
#endif
|
|
glUniform1i(uni->frame_direction, 1);
|
|
}
|
|
|
|
if (uni->frame_time_delta >= 0)
|
|
glUniform1i(uni->frame_time_delta, (GLint)video_driver_get_frame_time_delta_usec());
|
|
|
|
if (uni->original_fps >= 0)
|
|
glUniform1f(uni->original_fps, video_driver_get_original_fps());
|
|
|
|
if (uni->rotation >= 0)
|
|
glUniform1i(uni->rotation, video_driver_get_rotation_snapshot());
|
|
|
|
if (uni->core_aspect >= 0)
|
|
glUniform1f(uni->core_aspect, video_driver_get_core_aspect());
|
|
|
|
if (uni->core_aspect_rot >= 0)
|
|
{
|
|
/* OriginalAspectRotated: return 1/aspect for 90 and 270 rotated content */
|
|
float core_aspect_rot = video_driver_get_core_aspect();
|
|
uint32_t rot = video_driver_get_rotation_snapshot();
|
|
if (rot == 1 || rot == 3)
|
|
core_aspect_rot = 1/core_aspect_rot;
|
|
glUniform1f(uni->core_aspect_rot, core_aspect_rot);
|
|
}
|
|
|
|
/* Set lookup textures. */
|
|
for (i = 0; i < glsl->shader->luts; i++)
|
|
{
|
|
if (uni->lut_texture[i] < 0)
|
|
continue;
|
|
|
|
/* Have to rebind as HW render could override this. */
|
|
glActiveTexture(GL_TEXTURE0 + texunit);
|
|
glBindTexture(GL_TEXTURE_2D, glsl->lut_textures[i]);
|
|
glUniform1i(uni->lut_texture[i], texunit);
|
|
texunit++;
|
|
}
|
|
|
|
if (glsl->active_idx)
|
|
{
|
|
/* Set original texture. */
|
|
if (uni->orig.texture >= 0)
|
|
{
|
|
/* Bind original texture. */
|
|
glActiveTexture(GL_TEXTURE0 + texunit);
|
|
glUniform1i(uni->orig.texture, texunit);
|
|
glBindTexture(GL_TEXTURE_2D, info->tex);
|
|
texunit++;
|
|
}
|
|
|
|
if (uni->orig.texture_size >= 0)
|
|
glUniform2fv(uni->orig.texture_size, 1, info->tex_size);
|
|
|
|
if (uni->orig.input_size >= 0)
|
|
glUniform2fv(uni->orig.input_size, 1, info->input_size);
|
|
|
|
/* Pass texture coordinates. */
|
|
if (uni->orig.tex_coord >= 0)
|
|
{
|
|
attr->loc = uni->orig.tex_coord;
|
|
attr->size = 2;
|
|
attr->offset = (GLsizei)(size * sizeof(GLfloat));
|
|
attribs_size++;
|
|
attr++;
|
|
|
|
buffer[size ] = info->coord[0];
|
|
buffer[size + 1] = info->coord[1];
|
|
buffer[size + 2] = info->coord[2];
|
|
buffer[size + 3] = info->coord[3];
|
|
buffer[size + 4] = info->coord[4];
|
|
buffer[size + 5] = info->coord[5];
|
|
buffer[size + 6] = info->coord[6];
|
|
buffer[size + 7] = info->coord[7];
|
|
size += 8;
|
|
}
|
|
|
|
/* Set feedback texture. */
|
|
if (uni->feedback.texture >= 0)
|
|
{
|
|
/* Bind original texture. */
|
|
glActiveTexture(GL_TEXTURE0 + texunit);
|
|
glUniform1i(uni->feedback.texture, texunit);
|
|
glBindTexture(GL_TEXTURE_2D, feedback_info->tex);
|
|
texunit++;
|
|
}
|
|
|
|
if (uni->feedback.texture_size >= 0)
|
|
glUniform2fv(uni->feedback.texture_size, 1, feedback_info->tex_size);
|
|
|
|
if (uni->feedback.input_size >= 0)
|
|
glUniform2fv(uni->feedback.input_size, 1, feedback_info->input_size);
|
|
|
|
/* Pass texture coordinates. */
|
|
if (uni->feedback.tex_coord >= 0)
|
|
{
|
|
attr->loc = uni->feedback.tex_coord;
|
|
attr->size = 2;
|
|
attr->offset = (GLsizei)(size * sizeof(GLfloat));
|
|
attribs_size++;
|
|
attr++;
|
|
|
|
buffer[size ] = feedback_info->coord[0];
|
|
buffer[size + 1] = feedback_info->coord[1];
|
|
buffer[size + 2] = feedback_info->coord[2];
|
|
buffer[size + 3] = feedback_info->coord[3];
|
|
buffer[size + 4] = feedback_info->coord[4];
|
|
buffer[size + 5] = feedback_info->coord[5];
|
|
buffer[size + 6] = feedback_info->coord[6];
|
|
buffer[size + 7] = feedback_info->coord[7];
|
|
size += 8;
|
|
}
|
|
|
|
/* Bind FBO textures. */
|
|
for (i = 0; i < fbo_info_cnt; i++)
|
|
{
|
|
if (uni->pass[i].texture)
|
|
{
|
|
glActiveTexture(GL_TEXTURE0 + texunit);
|
|
glBindTexture(GL_TEXTURE_2D, fbo_info[i].tex);
|
|
glUniform1i(uni->pass[i].texture, texunit);
|
|
texunit++;
|
|
}
|
|
|
|
if (uni->pass[i].texture_size >= 0)
|
|
glUniform2fv(uni->pass[i].texture_size, 1, fbo_info[i].tex_size);
|
|
|
|
if (uni->pass[i].input_size >= 0)
|
|
glUniform2fv(uni->pass[i].input_size, 1, fbo_info[i].input_size);
|
|
|
|
if (uni->pass[i].tex_coord >= 0)
|
|
{
|
|
attr->loc = uni->pass[i].tex_coord;
|
|
attr->size = 2;
|
|
attr->offset = (GLsizei)(size * sizeof(GLfloat));
|
|
attribs_size++;
|
|
attr++;
|
|
|
|
buffer[size ] = fbo_info[i].coord[0];
|
|
buffer[size + 1] = fbo_info[i].coord[1];
|
|
buffer[size + 2] = fbo_info[i].coord[2];
|
|
buffer[size + 3] = fbo_info[i].coord[3];
|
|
buffer[size + 4] = fbo_info[i].coord[4];
|
|
buffer[size + 5] = fbo_info[i].coord[5];
|
|
buffer[size + 6] = fbo_info[i].coord[6];
|
|
buffer[size + 7] = fbo_info[i].coord[7];
|
|
size += 8;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Set previous textures. Only bind if they're actually used. */
|
|
for (i = 0; i < PREV_TEXTURES; i++)
|
|
{
|
|
if (uni->prev[i].texture >= 0)
|
|
{
|
|
glActiveTexture(GL_TEXTURE0 + texunit);
|
|
glBindTexture(GL_TEXTURE_2D, prev_info[i].tex);
|
|
glUniform1i(uni->prev[i].texture, texunit);
|
|
texunit++;
|
|
}
|
|
|
|
if (uni->prev[i].texture_size >= 0)
|
|
glUniform2fv(uni->prev[i].texture_size, 1, prev_info[i].tex_size);
|
|
|
|
if (uni->prev[i].input_size >= 0)
|
|
glUniform2fv(uni->prev[i].input_size, 1, prev_info[i].input_size);
|
|
|
|
/* Pass texture coordinates. */
|
|
if (uni->prev[i].tex_coord >= 0)
|
|
{
|
|
attr->loc = uni->prev[i].tex_coord;
|
|
attr->size = 2;
|
|
attr->offset = (GLsizei)(size * sizeof(GLfloat));
|
|
attribs_size++;
|
|
attr++;
|
|
|
|
buffer[size ] = prev_info[i].coord[0];
|
|
buffer[size + 1] = prev_info[i].coord[1];
|
|
buffer[size + 2] = prev_info[i].coord[2];
|
|
buffer[size + 3] = prev_info[i].coord[3];
|
|
buffer[size + 4] = prev_info[i].coord[4];
|
|
buffer[size + 5] = prev_info[i].coord[5];
|
|
buffer[size + 6] = prev_info[i].coord[6];
|
|
buffer[size + 7] = prev_info[i].coord[7];
|
|
size += 8;
|
|
}
|
|
}
|
|
|
|
if (size)
|
|
gl_glsl_set_attribs(glsl, glsl->vbo[glsl->active_idx].vbo_secondary,
|
|
&glsl->vbo[glsl->active_idx].buffer_secondary,
|
|
&glsl->vbo[glsl->active_idx].len_secondary,
|
|
buffer, size, attribs, attribs_size);
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
|
|
/* #pragma parameters. */
|
|
for (i = 0; i < glsl->shader->num_parameters; i++)
|
|
{
|
|
|
|
int location = glGetUniformLocation(
|
|
glsl->prg[glsl->active_idx].id,
|
|
glsl->shader->parameters[i].id);
|
|
glUniform1f(location,
|
|
video_shader_param_get(&glsl->shader->parameters[i]));
|
|
}
|
|
|
|
/* Sensor uniforms — values are 0.0 if sensors disabled or not available */
|
|
{
|
|
const struct shader_uniforms *uni = &glsl->uniforms[glsl->active_idx];
|
|
/* One coherent seqlock'd snapshot of the values
|
|
* input_driver_poll() published on the main thread. */
|
|
float gyro[3], accel[3], rest[3];
|
|
input_driver_read_sensor_snapshot(gyro, accel, rest);
|
|
|
|
if (uni->gyroscope >= 0)
|
|
glUniform3fv(uni->gyroscope, 1, gyro);
|
|
if (uni->accelerometer >= 0)
|
|
glUniform3fv(uni->accelerometer, 1, accel);
|
|
if (uni->accelerometer_rest >= 0)
|
|
glUniform3fv(uni->accelerometer_rest, 1, rest);
|
|
}
|
|
}
|
|
|
|
static bool gl_glsl_set_mvp(void *shader_data, const void *mat_data)
|
|
{
|
|
int loc;
|
|
glsl_shader_data_t *glsl = (glsl_shader_data_t*)shader_data;
|
|
|
|
if ( !glsl
|
|
|| (!(glsl->shader->flags & SHDR_FLAG_MODERN)))
|
|
return false;
|
|
|
|
loc = glsl->uniforms[glsl->active_idx].mvp;
|
|
if (loc >= 0)
|
|
{
|
|
const math_matrix_4x4 *mat = (const math_matrix_4x4*)mat_data;
|
|
|
|
if ( (glsl->current_idx != glsl->active_idx) ||
|
|
(mat->data != glsl->current_mat_data_pointer[glsl->active_idx]) ||
|
|
(*mat->data != glsl->current_mat_data[glsl->active_idx]))
|
|
{
|
|
glUniformMatrix4fv(loc, 1, GL_FALSE, mat->data);
|
|
glsl->current_idx = glsl->active_idx;
|
|
glsl->current_mat_data_pointer[glsl->active_idx] = (float*)mat->data;
|
|
glsl->current_mat_data[glsl->active_idx] = *mat->data;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#define gl_glsl_set_coord_array(attribs, coord1, coord2, coords, size, multiplier) \
|
|
unsigned y; \
|
|
attribs[attribs_size].loc = (GLint)coord1; \
|
|
attribs[attribs_size].size = (GLsizei)multiplier; \
|
|
attribs[attribs_size].offset = (GLsizei)(size * sizeof(GLfloat)); \
|
|
for (y = 0; y < (multiplier * coords->vertices); y++) \
|
|
buffer[y + size] = coord2[y]; \
|
|
size += multiplier * coords->vertices; \
|
|
|
|
static bool gl_glsl_set_coords(void *shader_data,
|
|
const struct video_coords *coords)
|
|
{
|
|
GLfloat short_buffer[4 * (2 + 2 + 4 + 2)];
|
|
struct glsl_attrib attribs[4];
|
|
size_t attribs_size = 0;
|
|
size_t size = 0;
|
|
GLfloat *buffer = short_buffer;
|
|
const GLfloat *static_src = NULL;
|
|
glsl_shader_data_t *glsl = (glsl_shader_data_t*)shader_data;
|
|
const struct shader_uniforms *uni = glsl
|
|
? &glsl->uniforms[glsl->active_idx] : NULL;
|
|
|
|
if ( !glsl
|
|
|| (!(glsl->shader->flags & SHDR_FLAG_MODERN))
|
|
|| !coords)
|
|
{
|
|
if (coords)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
if (coords->vertices > 4)
|
|
{
|
|
/* Avoid hitting malloc on every single regular quad draw. */
|
|
|
|
/* Must match the four bind conditions below exactly: a stream
|
|
* the caller did not supply is not bound and takes no room. */
|
|
size_t elems = 0;
|
|
elems += (uni->color >= 0 && coords->color) * 4;
|
|
elems += (uni->tex_coord >= 0 && coords->tex_coord) * 2;
|
|
elems += (uni->vertex_coord >= 0 && coords->vertex) * 2;
|
|
elems += (uni->lut_tex_coord >= 0 && coords->lut_tex_coord) * 2;
|
|
|
|
elems *= coords->vertices * sizeof(GLfloat);
|
|
|
|
if (elems > glsl->coord_scratch_cap)
|
|
{
|
|
GLfloat *grown = (GLfloat*)realloc(glsl->coord_scratch, elems);
|
|
if (!grown)
|
|
return false;
|
|
glsl->coord_scratch = grown;
|
|
glsl->coord_scratch_cap = elems;
|
|
}
|
|
buffer = glsl->coord_scratch;
|
|
}
|
|
|
|
#if defined(VITA)
|
|
if (uni->time >= 0)
|
|
{
|
|
float t = (sceKernelGetSystemTimeWide()) / (scePowerGetArmClockFrequency() * 1000.0);
|
|
glUniform1f(uni->time, t);
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_SHADERPIPELINE
|
|
/* The ribbon pipelines draw one grid the menu driver builds once and
|
|
* hands back every frame, as their only attribute. When the buffer
|
|
* already holds that array, bind it as it is: no copy into the
|
|
* scratch, no comparison against the last upload. */
|
|
if ( ( glsl->active_idx == VIDEO_SHADER_MENU
|
|
|| glsl->active_idx == VIDEO_SHADER_MENU_2)
|
|
&& uni->vertex_coord >= 0 && coords->vertex && coords->vertices >= 4
|
|
&& (uni->tex_coord < 0 || !coords->tex_coord)
|
|
&& (uni->color < 0 || !coords->color)
|
|
&& (uni->lut_tex_coord < 0 || !coords->lut_tex_coord))
|
|
{
|
|
struct cache_vbo *cache = &glsl->vbo[glsl->active_idx];
|
|
const GLfloat *tail = coords->vertex + 2 * coords->vertices - 4;
|
|
|
|
if ( cache->static_src == coords->vertex
|
|
&& cache->static_vertices == coords->vertices
|
|
&& !memcmp(cache->static_head, coords->vertex,
|
|
sizeof(cache->static_head))
|
|
&& !memcmp(cache->static_tail, tail,
|
|
sizeof(cache->static_tail)))
|
|
{
|
|
if (glsl->attribs_index < ARRAY_SIZE(glsl->attribs_elems))
|
|
{
|
|
glBindBuffer(GL_ARRAY_BUFFER, cache->vbo_primary);
|
|
glEnableVertexAttribArray(uni->vertex_coord);
|
|
glVertexAttribPointer(uni->vertex_coord, 2, GL_FLOAT,
|
|
GL_FALSE, 0, (const GLvoid*)0);
|
|
glsl->attribs_elems[glsl->attribs_index++] = uni->vertex_coord;
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* Uploaded the usual way below, then known by its address */
|
|
static_src = coords->vertex;
|
|
}
|
|
#endif
|
|
|
|
|
|
if (uni->tex_coord >= 0 && coords->tex_coord)
|
|
{
|
|
gl_glsl_set_coord_array(attribs, uni->tex_coord,
|
|
coords->tex_coord, coords, size, 2);
|
|
attribs_size++;
|
|
}
|
|
|
|
if (uni->vertex_coord >= 0 && coords->vertex)
|
|
{
|
|
gl_glsl_set_coord_array(attribs, uni->vertex_coord,
|
|
coords->vertex, coords, size, 2);
|
|
attribs_size++;
|
|
}
|
|
|
|
if (uni->color >= 0 && coords->color)
|
|
{
|
|
gl_glsl_set_coord_array(attribs, uni->color,
|
|
coords->color, coords, size, 4);
|
|
attribs_size++;
|
|
}
|
|
|
|
if (uni->lut_tex_coord >= 0 && coords->lut_tex_coord)
|
|
{
|
|
gl_glsl_set_coord_array(attribs, uni->lut_tex_coord,
|
|
coords->lut_tex_coord, coords, size, 2);
|
|
attribs_size++;
|
|
}
|
|
|
|
if (size)
|
|
gl_glsl_set_attribs(glsl,
|
|
glsl->vbo[glsl->active_idx].vbo_primary,
|
|
&glsl->vbo[glsl->active_idx].buffer_primary,
|
|
&glsl->vbo[glsl->active_idx].len_primary,
|
|
buffer, size,
|
|
attribs, attribs_size);
|
|
|
|
{
|
|
/* Whatever went in last is what the buffer holds now */
|
|
struct cache_vbo *cache = &glsl->vbo[glsl->active_idx];
|
|
cache->static_src = static_src;
|
|
if (static_src)
|
|
{
|
|
cache->static_vertices = coords->vertices;
|
|
memcpy(cache->static_head, static_src, sizeof(cache->static_head));
|
|
memcpy(cache->static_tail, static_src + 2 * coords->vertices - 4,
|
|
sizeof(cache->static_tail));
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void gl_glsl_compile_menu_shader(glsl_shader_data_t *glsl, unsigned idx);
|
|
|
|
/* Which bit of menu_shaders_tried a menu effect has; 0 for anything else */
|
|
static uint8_t gl_glsl_menu_shader_bit(unsigned idx)
|
|
{
|
|
#ifdef HAVE_SHADERPIPELINE
|
|
switch (idx)
|
|
{
|
|
case VIDEO_SHADER_MENU: return 1 << 0;
|
|
case VIDEO_SHADER_MENU_2: return 1 << 1;
|
|
case VIDEO_SHADER_MENU_3: return 1 << 2;
|
|
case VIDEO_SHADER_MENU_4: return 1 << 3;
|
|
case VIDEO_SHADER_MENU_5: return 1 << 4;
|
|
case VIDEO_SHADER_MENU_6: return 1 << 5;
|
|
default:
|
|
break;
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static void gl_glsl_use(void *data, void *shader_data, unsigned idx, bool set_active)
|
|
{
|
|
GLuint id;
|
|
|
|
if (set_active)
|
|
{
|
|
glsl_shader_data_t *glsl = (glsl_shader_data_t*)shader_data;
|
|
if (!glsl)
|
|
return;
|
|
|
|
{
|
|
uint8_t bit = gl_glsl_menu_shader_bit(idx);
|
|
if (bit && !(glsl->menu_shaders_tried & bit))
|
|
{
|
|
glsl->menu_shaders_tried |= bit;
|
|
gl_glsl_compile_menu_shader(glsl, idx);
|
|
}
|
|
}
|
|
gl_glsl_reset_attrib(glsl);
|
|
glsl->active_idx = idx;
|
|
id = glsl->prg[idx].id;
|
|
}
|
|
else
|
|
id = (GLuint)idx;
|
|
|
|
glUseProgram(id);
|
|
}
|
|
|
|
static unsigned gl_glsl_num(void *data)
|
|
{
|
|
glsl_shader_data_t *glsl = (glsl_shader_data_t*)data;
|
|
if (glsl && glsl->shader)
|
|
return glsl->shader->passes;
|
|
return 0;
|
|
}
|
|
|
|
static bool gl_glsl_filter_type(void *data, unsigned idx, bool *smooth)
|
|
{
|
|
glsl_shader_data_t *glsl = (glsl_shader_data_t*)data;
|
|
if (glsl && idx
|
|
&& (glsl->shader->pass[idx - 1].filter != RARCH_FILTER_UNSPEC)
|
|
)
|
|
{
|
|
*smooth = (glsl->shader->pass[idx - 1].filter == RARCH_FILTER_LINEAR);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static enum gfx_wrap_type gl_glsl_wrap_type(void *data, unsigned idx)
|
|
{
|
|
glsl_shader_data_t *glsl = (glsl_shader_data_t*)data;
|
|
if (glsl && idx)
|
|
return glsl->shader->pass[idx - 1].wrap;
|
|
return RARCH_WRAP_BORDER;
|
|
}
|
|
|
|
static void gl_glsl_shader_scale(void *data, unsigned idx, struct gfx_fbo_scale *scale)
|
|
{
|
|
glsl_shader_data_t *glsl = (glsl_shader_data_t*)data;
|
|
if (glsl && idx)
|
|
*scale = glsl->shader->pass[idx - 1].fbo;
|
|
else
|
|
scale->flags &= ~FBO_SCALE_FLAG_VALID;
|
|
}
|
|
|
|
static unsigned gl_glsl_get_prev_textures(void *data)
|
|
{
|
|
unsigned i, j;
|
|
unsigned max_prev = 0;
|
|
glsl_shader_data_t *glsl = (glsl_shader_data_t*)data;
|
|
|
|
if (!glsl)
|
|
return 0;
|
|
|
|
for (i = 1; i <= glsl->shader->passes; i++)
|
|
for (j = 0; j < PREV_TEXTURES; j++)
|
|
if (glsl->uniforms[i].prev[j].texture >= 0)
|
|
max_prev = MAX(j + 1, max_prev);
|
|
|
|
return max_prev;
|
|
}
|
|
|
|
static bool gl_glsl_mipmap_input(void *data, unsigned idx)
|
|
{
|
|
glsl_shader_data_t *glsl = (glsl_shader_data_t*)data;
|
|
if (glsl && idx)
|
|
return glsl->shader->pass[idx - 1].mipmap;
|
|
return false;
|
|
}
|
|
|
|
static bool gl_glsl_get_feedback_pass(void *data, unsigned *index)
|
|
{
|
|
glsl_shader_data_t *glsl = (glsl_shader_data_t*)data;
|
|
if (!glsl || glsl->shader->feedback_pass < 0)
|
|
return false;
|
|
|
|
*index = glsl->shader->feedback_pass;
|
|
return true;
|
|
}
|
|
|
|
static struct video_shader *gl_glsl_get_current_shader(void *data)
|
|
{
|
|
glsl_shader_data_t *glsl = (glsl_shader_data_t*)data;
|
|
if (!glsl)
|
|
return NULL;
|
|
return glsl->shader;
|
|
}
|
|
|
|
static void gl_glsl_get_flags(uint32_t *flags)
|
|
{
|
|
BIT32_SET(*flags, GFX_CTX_FLAGS_SHADERS_GLSL);
|
|
}
|
|
|
|
void gl_glsl_set_context_type(bool core_profile,
|
|
unsigned major, unsigned minor)
|
|
{
|
|
glsl_core = core_profile;
|
|
glsl_major = major;
|
|
glsl_minor = minor;
|
|
}
|
|
|
|
|
|
bool gl_glsl_draw_mesh(void *shader_data, unsigned vbo, unsigned ibo,
|
|
unsigned count, bool strip, const float *mvp, const float *tint)
|
|
{
|
|
static const char *attr_names[3] = { "VertexCoord", "TexCoord", "Color" };
|
|
static const GLint attr_sizes[3] = { 3, 2, 4 };
|
|
static const GLenum attr_types[3] = { GL_FLOAT, GL_UNSIGNED_SHORT,
|
|
GL_UNSIGNED_BYTE };
|
|
static const size_t attr_offsets[3] = { 0, 12, 16 };
|
|
glsl_shader_data_t *glsl = (glsl_shader_data_t*)shader_data;
|
|
GLenum mode = strip ? GL_TRIANGLE_STRIP : GL_TRIANGLES;
|
|
unsigned i;
|
|
|
|
if (!glsl || glsl->mesh_failed)
|
|
return false;
|
|
if (!glsl->mesh_prg)
|
|
{
|
|
struct shader_program_glsl_data program;
|
|
struct shader_program_info info;
|
|
memset(&program, 0, sizeof(program));
|
|
info.data = NULL;
|
|
info.vertex = glsl_core ? stock_vertex_mesh_core : stock_vertex_mesh_legacy;
|
|
info.fragment = glsl_core ? stock_fragment_mesh_core : stock_fragment_mesh_legacy;
|
|
info.combined = NULL;
|
|
info.idx = 0;
|
|
info.is_file = false;
|
|
if (!gl_glsl_compile_program(glsl, 0, &program, &info) || !program.id)
|
|
{
|
|
glsl->mesh_failed = true;
|
|
return false;
|
|
}
|
|
glsl->mesh_prg = program.id;
|
|
glsl->mesh_mvp = glGetUniformLocation(program.id, "MVPMatrix");
|
|
glsl->mesh_tint = glGetUniformLocation(program.id, "Tint");
|
|
for (i = 0; i < 3; i++)
|
|
glsl->mesh_attr[i] = glGetAttribLocation(program.id, attr_names[i]);
|
|
if (glsl->mesh_mvp < 0 || glsl->mesh_attr[0] < 0)
|
|
{
|
|
glDeleteProgram(glsl->mesh_prg);
|
|
glsl->mesh_prg = 0;
|
|
glsl->mesh_failed = true;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
glUseProgram(glsl->mesh_prg);
|
|
glUniformMatrix4fv(glsl->mesh_mvp, 1, GL_FALSE, mvp);
|
|
if (glsl->mesh_tint >= 0)
|
|
glUniform4fv(glsl->mesh_tint, 1, tint);
|
|
|
|
/* Read as the mesh stores it, 20 bytes a vertex */
|
|
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
if (glsl->mesh_attr[i] < 0)
|
|
continue;
|
|
glEnableVertexAttribArray(glsl->mesh_attr[i]);
|
|
glVertexAttribPointer(glsl->mesh_attr[i], attr_sizes[i],
|
|
attr_types[i], i ? GL_TRUE : GL_FALSE, 20,
|
|
(const GLvoid*)(uintptr_t)attr_offsets[i]);
|
|
}
|
|
if (ibo)
|
|
{
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
|
|
glDrawElements(mode, count, GL_UNSIGNED_SHORT, (const GLvoid*)0);
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
|
}
|
|
else
|
|
glDrawArrays(mode, 0, count);
|
|
for (i = 0; i < 3; i++)
|
|
if (glsl->mesh_attr[i] >= 0)
|
|
glDisableVertexAttribArray(glsl->mesh_attr[i]);
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
|
|
/* Whatever the caller draws next draws with its own program */
|
|
glUseProgram(glsl->prg[glsl->active_idx].id);
|
|
return true;
|
|
}
|
|
|
|
const shader_backend_t gl_glsl_backend = {
|
|
gl_glsl_init,
|
|
gl_glsl_init_menu_shaders,
|
|
gl_glsl_deinit,
|
|
gl_glsl_set_params,
|
|
gl_glsl_set_uniform_parameter,
|
|
gl_glsl_compile_program,
|
|
gl_glsl_use,
|
|
gl_glsl_num,
|
|
gl_glsl_filter_type,
|
|
gl_glsl_wrap_type,
|
|
gl_glsl_shader_scale,
|
|
gl_glsl_set_coords,
|
|
gl_glsl_set_mvp,
|
|
gl_glsl_get_prev_textures,
|
|
gl_glsl_get_feedback_pass,
|
|
gl_glsl_mipmap_input,
|
|
gl_glsl_get_current_shader,
|
|
gl_glsl_get_flags,
|
|
|
|
RARCH_SHADER_GLSL,
|
|
"glsl"
|
|
};
|