efl/src/modules/evas/engines/gl_common/evas_gl_shader.c

1049 lines
29 KiB
C

#include "config.h"
#include "shader/evas_gl_shaders.x"
#include "evas_gl_common.h"
#define SHADER_FLAG_SAM_BITSHIFT 3
#define SHADER_FLAG_MASKSAM_BITSHIFT 6
#define SHADER_PROG_NAME_FMT "/shader/%08x"
#define SHADER_BINARY_EET_COMPRESS 1
#define SHADER_EET_CHECKSUM "/shader/checksum"
#define SHADER_EET_CACHENAME "binary_shader"
#define P(i) ((void*)(intptr_t)(i))
#define I(p) ((int)(intptr_t)(p))
#ifdef WORDS_BIGENDIAN
# define BASEFLAG SHADER_FLAG_DITHER | SHADER_FLAG_BIGENDIAN
#else
# define BASEFLAG SHADER_FLAG_DITHER
#endif
typedef enum {
SHADER_FLAG_TEX = (1 << 0),
SHADER_FLAG_BGRA = (1 << 1),
SHADER_FLAG_MASK = (1 << 2),
SHADER_FLAG_SAM12 = (1 << (SHADER_FLAG_SAM_BITSHIFT + 0)),
SHADER_FLAG_SAM21 = (1 << (SHADER_FLAG_SAM_BITSHIFT + 1)),
SHADER_FLAG_SAM22 = (1 << (SHADER_FLAG_SAM_BITSHIFT + 2)),
SHADER_FLAG_MASKSAM12 = (1 << (SHADER_FLAG_MASKSAM_BITSHIFT + 0)),
SHADER_FLAG_MASKSAM21 = (1 << (SHADER_FLAG_MASKSAM_BITSHIFT + 1)),
SHADER_FLAG_MASKSAM22 = (1 << (SHADER_FLAG_MASKSAM_BITSHIFT + 2)),
SHADER_FLAG_MASK_COLOR = (1 << 9),
SHADER_FLAG_IMG = (1 << 10),
SHADER_FLAG_BIGENDIAN = (1 << 11),
SHADER_FLAG_YUV = (1 << 12),
SHADER_FLAG_YUY2 = (1 << 13),
SHADER_FLAG_NV12 = (1 << 14),
SHADER_FLAG_YUV_709 = (1 << 15),
SHADER_FLAG_EXTERNAL = (1 << 16),
SHADER_FLAG_AFILL = (1 << 17),
SHADER_FLAG_NOMUL = (1 << 18),
SHADER_FLAG_ALPHA = (1 << 19),
SHADER_FLAG_RGB_A_PAIR = (1 << 20),
SHADER_FLAG_FILTER_DISPLACE = (1 << 21),
SHADER_FLAG_FILTER_CURVE = (1 << 22),
SHADER_FLAG_FILTER_BLUR = (1 << 23),
SHADER_FLAG_FILTER_DIR_Y = (1 << 24),
SHADER_FLAG_FILTER_ALPHA_ONLY = (1 << 25),
SHADER_FLAG_FILTER_GRAYSCALE = (1 << 26),
SHADER_FLAG_FILTER_INVERSE_COLOR = (1 << 27),
SHADER_FLAG_DITHER = (1 << 28),
} Shader_Flag;
#define SHADER_FLAG_COUNT 29
static const char *_shader_flags[SHADER_FLAG_COUNT] = {
"TEX",
"BGRA",
"MASK",
"SAM12",
"SAM21",
"SAM22",
"MASKSAM12",
"MASKSAM21",
"MASKSAM22",
"MASK_COLOR",
"IMG",
"BIGENDIAN",
"YUV",
"YUY2",
"NV12",
"YUV_709",
"EXTERNAL",
"AFILL",
"NOMUL",
"ALPHA",
"RGB_A_PAIR",
"FILTER_DISPLACE",
"FILTER_CURVE",
"FILTER_BLUR",
"FILTER_DIR_Y",
"ALPHA_ONLY",
"FILTER_GRAYSCALE",
"FILTER_INVERSE_COLOR",
"DITHER",
};
static Eina_Bool compiler_released = EINA_FALSE;
static Eina_Bool _do_dither = EINA_TRUE;
static void
gl_compile_link_error(GLuint target, const char *action, Eina_Bool is_shader)
{
int loglen = 0, chars = 0;
char *logtxt;
if (is_shader)
/* Shader info log */
glGetShaderiv(target, GL_INFO_LOG_LENGTH, &loglen);
else
/* Program info log */
glGetProgramiv(target, GL_INFO_LOG_LENGTH, &loglen);
if (loglen > 0)
{
logtxt = calloc(loglen, sizeof(char));
if (logtxt)
{
if (is_shader) glGetShaderInfoLog(target, loglen, &chars, logtxt);
else glGetProgramInfoLog(target, loglen, &chars, logtxt);
ERR("Failed to %s: %s", action, logtxt);
free(logtxt);
}
}
}
static inline void
_attributes_bind(GLint prg)
{
glBindAttribLocation(prg, SHAD_VERTEX, "vertex");
glBindAttribLocation(prg, SHAD_COLOR, "color");
glBindAttribLocation(prg, SHAD_TEXUV, "tex_coord");
glBindAttribLocation(prg, SHAD_TEXUV2, "tex_coord2");
glBindAttribLocation(prg, SHAD_TEXUV3, "tex_coord3");
glBindAttribLocation(prg, SHAD_TEXA, "tex_coorda");
glBindAttribLocation(prg, SHAD_TEXSAM, "tex_sample");
glBindAttribLocation(prg, SHAD_MASK, "mask_coord");
glBindAttribLocation(prg, SHAD_MASKSAM, "tex_masksample");
}
static Evas_GL_Program *
_evas_gl_common_shader_program_binary_load(Eet_File *ef, unsigned int flags)
{
int num = 0, length = 0;
int *formats = NULL;
void *data = NULL;
char pname[32];
GLint ok = 0, prg, vtx = GL_NONE, frg = GL_NONE;
Evas_GL_Program *p = NULL;
Eina_Bool direct = 1;
if (!ef || !glsym_glProgramBinary) return NULL;
sprintf(pname, SHADER_PROG_NAME_FMT, flags);
data = (void *) eet_read_direct(ef, pname, &length);
if (!data)
{
data = eet_read(ef, pname, &length);
direct = 0;
}
if ((!data) || (length <= 0)) goto finish;
glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &num);
if (num <= 0) goto finish;
formats = calloc(num, sizeof(int));
if (!formats) goto finish;
glGetIntegerv(GL_PROGRAM_BINARY_FORMATS, formats);
if (!formats[0]) goto finish;
prg = glCreateProgram();
#if 1
// TODO: invalid rendering error occurs when attempting to use a
// glProgramBinary. in order to render correctly we should create a dummy
// vertex shader.
vtx = glCreateShader(GL_VERTEX_SHADER);
glAttachShader(prg, vtx);
frg = glCreateShader(GL_FRAGMENT_SHADER);
glAttachShader(prg, frg);
#endif
glsym_glProgramBinary(prg, formats[0], data, length);
_attributes_bind(prg);
glGetProgramiv(prg, GL_LINK_STATUS, &ok);
if (!ok)
{
gl_compile_link_error(prg, "load a program object", EINA_FALSE);
ERR("Abort load of program (%s)", pname);
glDeleteProgram(prg);
goto finish;
}
p = calloc(1, sizeof(*p));
GLuint curr_prog = 0;
glGetIntegerv(GL_CURRENT_PROGRAM, (GLint *)&curr_prog);
p->flags = flags;
p->prog = prg;
p->reset = EINA_TRUE;
p->bin_saved = EINA_TRUE;
glUseProgram(prg);
p->uniform.mvp = glGetUniformLocation(prg, "mvp");
p->uniform.rotation_id = glGetUniformLocation(prg, "rotation_id");
evas_gl_common_shader_textures_bind(p, EINA_FALSE);
glUseProgram(curr_prog);
finish:
if (vtx) glDeleteShader(vtx);
if (frg) glDeleteShader(frg);
free(formats);
if (!direct) free(data);
return p;
}
static int
_evas_gl_common_shader_program_binary_save(Evas_GL_Program *p, Eet_File *ef)
{
void* data = NULL;
GLenum format;
int length = 0, size = 0;
char pname[32];
if (!glsym_glGetProgramBinary) return 0;
glGetProgramiv(p->prog, GL_PROGRAM_BINARY_LENGTH, &length);
if (length <= 0) return 0;
data = malloc(length);
if (!data) return 0;
glsym_glGetProgramBinary(p->prog, length, &size, &format, data);
if (length != size)
{
free(data);
return 0;
}
sprintf(pname, SHADER_PROG_NAME_FMT, p->flags);
if (eet_write(ef, pname, data, length, SHADER_BINARY_EET_COMPRESS) < 0)
{
free(data);
return 0;
}
free(data);
p->bin_saved = 1;
return 1;
}
static void
_evas_gl_common_shader_binary_hash(Evas_GL_Shared *shared)
{
if (shared->shaders_checksum)
return;
/* This hash makes it sure that if the shaders code changes, then we
* will not reuse the old binaries. */
shared->shaders_checksum = eina_stringshare_printf
("%#x:%#x",
eina_hash_superfast(fragment_glsl, strlen(fragment_glsl)),
eina_hash_superfast(vertex_glsl, strlen(vertex_glsl)));
}
static Eina_Bool
_evas_gl_common_shader_binary_checksum_check(Evas_GL_Shared *shared, Eet_File *ef)
{
Eina_Bool ret = EINA_FALSE;
const char *old_hash;
int len = 0;
if (!ef) return EINA_FALSE;
_evas_gl_common_shader_binary_hash(shared);
old_hash = eet_read_direct(ef, SHADER_EET_CHECKSUM, &len);
if (old_hash &&
(len == (eina_stringshare_strlen(shared->shaders_checksum) + 1)) &&
(!strcmp(shared->shaders_checksum, old_hash)))
ret = EINA_TRUE;
return ret;
}
static Eina_Bool
_evas_gl_common_shader_binary_checksum_write(Evas_GL_Shared *shared, Eet_File *ef)
{
int ret, len;
if (!ef) return EINA_FALSE;
_evas_gl_common_shader_binary_hash(shared);
len = eina_stringshare_strlen(shared->shaders_checksum) + 1;
ret = eet_write(ef, SHADER_EET_CHECKSUM, shared->shaders_checksum, len, 0);
return (ret == len);
}
static int
_evas_gl_common_shader_binary_init(Evas_GL_Shared *shared)
{
Eet_File *ef = NULL;
char bin_dir_path[PATH_MAX];
char bin_file_path[PATH_MAX];
if (!shared || !shared->info.bin_program)
return 1;
if (shared->shaders_cache)
return 1;
if (!evas_gl_common_file_cache_dir_check(bin_dir_path, sizeof(bin_dir_path)))
return 0;
if (!evas_gl_common_file_cache_file_check(bin_dir_path, SHADER_EET_CACHENAME,
bin_file_path, sizeof(bin_dir_path)))
return 0;
if (!eet_init()) return 0;
ef = eet_open(bin_file_path, EET_FILE_MODE_READ);
if (!_evas_gl_common_shader_binary_checksum_check(shared, ef))
goto error;
shared->shaders_cache = ef;
return 1;
error:
if (ef) eet_close(ef);
eet_shutdown();
return 0;
}
static int
_evas_gl_common_shader_binary_save(Evas_GL_Shared *shared)
{
char bin_dir_path[PATH_MAX];
char bin_file_path[PATH_MAX];
char tmp_file_name[PATH_MAX + PATH_MAX + 128];
int tmpfd = -1, copy;
Eina_Tmpstr *tmp_file_path = NULL;
Eet_File *ef = NULL, *ef0 = NULL;
Evas_GL_Program *p;
Eina_Iterator *it;
/* use eet */
if (!eet_init()) return 0;
if (!evas_gl_common_file_cache_dir_check(bin_dir_path, sizeof(bin_dir_path)))
{
if (!evas_gl_common_file_cache_mkpath(bin_dir_path))
return 0; /* we can't make directory */
}
copy = evas_gl_common_file_cache_file_check(bin_dir_path, SHADER_EET_CACHENAME,
bin_file_path, sizeof(bin_dir_path));
/* use mkstemp for writing */
snprintf(tmp_file_name, sizeof(tmp_file_name), "%s.XXXXXX.cache", bin_file_path);
tmpfd = eina_file_mkstemp(tmp_file_name, &tmp_file_path);
if (tmpfd < 0) goto error;
/* copy old file */
if (copy)
{
ef = eet_open(tmp_file_path, EET_FILE_MODE_READ);
if (!ef) goto save;
if (!_evas_gl_common_shader_binary_checksum_check(shared, ef))
copy = EINA_FALSE;
eet_close(ef);
if (copy)
eina_file_copy(bin_file_path, tmp_file_path, EINA_FILE_COPY_DATA, NULL, NULL);
}
save:
ef = eet_open(tmp_file_path, EET_FILE_MODE_WRITE);
if (!ef) goto error;
if (copy) ef0 = shared->shaders_cache;
if (!_evas_gl_common_shader_binary_checksum_write(shared, ef))
goto error;
if (ef0)
{
char **keys;
int keys_num = 0, i;
keys = eet_list(ef0, "/shader/*", &keys_num);
if (keys)
{
for (i = 0; i < keys_num; i++)
{
int len = 0;
void *data = eet_read(ef0, keys[i], &len);
if ((data) && (len > 0))
eet_write(ef, keys[i], data, len, SHADER_BINARY_EET_COMPRESS);
free(data);
}
free(keys);
}
}
it = eina_hash_iterator_data_new(shared->shaders_hash);
EINA_ITERATOR_FOREACH(it, p)
{
if (!p->bin_saved)
{
if (_evas_gl_common_shader_program_binary_save(p, ef))
p->bin_saved = 1;
}
}
eina_iterator_free(it);
if (shared->shaders_cache)
{
eet_close(shared->shaders_cache);
shared->shaders_cache = NULL;
eet_shutdown();
}
if (eet_close(ef) != EET_ERROR_NONE) goto destroyed;
if (rename(tmp_file_path, bin_file_path) < 0) goto destroyed;
eina_tmpstr_del(tmp_file_path);
close(tmpfd);
eet_shutdown();
shared->needs_shaders_flush = 0;
return 1;
destroyed:
ef = NULL;
error:
if (tmpfd >= 0) close(tmpfd);
if (ef) eet_close(ef);
if (evas_gl_common_file_cache_file_exists(tmp_file_path))
unlink(tmp_file_path);
eina_tmpstr_del(tmp_file_path);
eet_shutdown();
return 0;
}
static inline void
_program_del(Evas_GL_Program *p)
{
if (p->filter)
{
if (p->filter->texture.tex_ids[0])
glDeleteTextures(1, p->filter->texture.tex_ids);
free(p->filter);
}
if (p->prog) glDeleteProgram(p->prog);
free(p);
}
static void
_shaders_hash_free_cb(void *data)
{
_program_del(data);
}
static char *
evas_gl_common_shader_glsl_get(unsigned int flags, const char *base)
{
Eina_Strbuf *s = eina_strbuf_new();
unsigned int k;
char *str;
/* This is an env var to use for debugging purposes only */
static const char *evas_gl_shader_glsl_version = NULL;
if (!evas_gl_shader_glsl_version)
{
evas_gl_shader_glsl_version = getenv("EVAS_GL_SHADER_GLSL_VERSION");
if (!evas_gl_shader_glsl_version) evas_gl_shader_glsl_version = "";
else WRN("Using GLSL version tag: '%s'", evas_gl_shader_glsl_version);
}
if (*evas_gl_shader_glsl_version)
eina_strbuf_append_printf(s, "#version %s\n", evas_gl_shader_glsl_version);
for (k = 0; k < SHADER_FLAG_COUNT; k++)
{
if (flags & (1 << k))
eina_strbuf_append_printf(s, "#define SHD_%s\n", _shader_flags[k]);
}
eina_strbuf_append(s, base);
str = eina_strbuf_string_steal(s);
eina_strbuf_free(s);
return str;
}
static Evas_GL_Program *
evas_gl_common_shader_compile(unsigned int flags, const char *vertex,
const char *fragment)
{
Evas_GL_Program *p;
GLuint vtx, frg, prg;
GLint ok = 0;
compiler_released = EINA_FALSE;
vtx = glCreateShader(GL_VERTEX_SHADER);
frg = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(vtx, 1, &vertex, NULL);
glCompileShader(vtx);
glGetShaderiv(vtx, GL_COMPILE_STATUS, &ok);
if (!ok)
{
gl_compile_link_error(vtx, "compile vertex shader", EINA_TRUE);
ERR("Abort compile of vertex shader:\n%s", vertex);
glDeleteShader(vtx);
return NULL;
}
ok = 0;
glShaderSource(frg, 1, &fragment, NULL);
glCompileShader(frg);
glGetShaderiv(frg, GL_COMPILE_STATUS, &ok);
if (!ok)
{
gl_compile_link_error(frg, "compile fragment shader", EINA_TRUE);
ERR("Abort compile of fragment shader:\n%s", fragment);
glDeleteShader(vtx);
glDeleteShader(frg);
return NULL;
}
ok = 0;
prg = glCreateProgram();
#ifndef GL_GLES
if ((glsym_glGetProgramBinary) && (glsym_glProgramParameteri))
glsym_glProgramParameteri(prg, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_TRUE);
#endif
glAttachShader(prg, vtx);
glAttachShader(prg, frg);
_attributes_bind(prg);
glLinkProgram(prg);
glGetProgramiv(prg, GL_LINK_STATUS, &ok);
if (!ok)
{
gl_compile_link_error(prg, "link fragment and vertex shaders", EINA_FALSE);
ERR("Abort compile of shader (flags: %08x)", flags);
glDeleteShader(vtx);
glDeleteShader(frg);
glDeleteProgram(prg);
return 0;
}
p = calloc(1, sizeof(*p));
p->flags = flags;
p->prog = prg;
p->reset = EINA_TRUE;
glDeleteShader(vtx);
glDeleteShader(frg);
return p;
}
static Eina_List *
evas_gl_common_shader_precompile_list(Evas_GL_Shared *shared)
{
int bgra, mask, sam, masksam, img, nomul, afill, yuv;
Eina_List *li = NULL;
unsigned int baseflags = 0;
if (!shared) return NULL;
if (_do_dither) baseflags |= BASEFLAG;
// rect
li = eina_list_append(li, P(baseflags));
// text
for (mask = 0; mask <= 1; mask++)
for (masksam = SHD_SAM11; masksam < (mask ? SHD_SAM_LAST : 1); masksam++)
{
int flags = baseflags | SHADER_FLAG_TEX | SHADER_FLAG_ALPHA;
if (mask) flags |= SHADER_FLAG_MASK;
if (masksam) flags |= (1 << (SHADER_FLAG_MASKSAM_BITSHIFT + masksam - 1));
li = eina_list_append(li, P(flags));
}
// images
for (mask = 0; mask <= 1; mask++)
for (masksam = SHD_SAM11; masksam < (mask ? SHD_SAM_LAST : 1); masksam++)
for (sam = SHD_SAM11; sam < SHD_SAM_LAST; sam++)
for (bgra = 0; bgra <= shared->info.bgra; bgra++)
for (img = 0; img <= 1; img++)
for (nomul = 0; nomul <= 1; nomul++)
for (afill = 0; afill <= (mask ? 0 : 1); afill++)
{
int flags = baseflags | SHADER_FLAG_TEX;
if (mask) flags |= SHADER_FLAG_MASK;
if (masksam) flags |= (1 << (SHADER_FLAG_MASKSAM_BITSHIFT + masksam - 1));
if (sam) flags |= (1 << (SHADER_FLAG_SAM_BITSHIFT + sam - 1));
if (bgra) flags |= SHADER_FLAG_BGRA;
if (img) flags |= SHADER_FLAG_IMG;
if (nomul) flags |= SHADER_FLAG_NOMUL;
if (afill) flags |= SHADER_FLAG_AFILL;
li = eina_list_append(li, P(flags));
}
// yuv
for (yuv = SHADER_FLAG_YUV; yuv <= SHADER_FLAG_YUV_709; yuv *= 2)
for (mask = 0; mask <= 1; mask++)
for (masksam = SHD_SAM11; masksam < (mask ? SHD_SAM_LAST : 1); masksam++)
for (nomul = 0; nomul <= 1; nomul++)
{
int flags = baseflags | SHADER_FLAG_TEX | yuv;
if (mask) flags |= SHADER_FLAG_MASK;
if (masksam) flags |= (1 << (SHADER_FLAG_MASKSAM_BITSHIFT + masksam - 1));
if (yuv == SHADER_FLAG_YUV_709) flags |= SHADER_FLAG_YUV;
if (nomul) flags |= SHADER_FLAG_NOMUL;
li = eina_list_append(li, P(flags));
}
// rgb+a pair, external, and others will not be precompiled.
DBG("Built list of %d shaders to precompile", eina_list_count(li));
return li;
}
static Evas_GL_Program *
evas_gl_common_shader_generate_and_compile(Evas_GL_Shared *shared, unsigned int flags)
{
char *vertex, *fragment;
Evas_GL_Program *p;
if (eina_hash_find(shared->shaders_hash, &flags))
return NULL;
vertex = evas_gl_common_shader_glsl_get(flags, vertex_glsl);
fragment = evas_gl_common_shader_glsl_get(flags, fragment_glsl);
p = evas_gl_common_shader_compile(flags, vertex, fragment);
if (p)
{
shared->needs_shaders_flush = 1;
p->uniform.mvp = glGetUniformLocation(p->prog, "mvp");
p->uniform.rotation_id = glGetUniformLocation(p->prog, "rotation_id");
evas_gl_common_shader_textures_bind(p, EINA_TRUE);
eina_hash_add(shared->shaders_hash, &flags, p);
}
else WRN("Failed to compile a shader (flags: %08x)", flags);
free(vertex);
free(fragment);
return p;
}
static int
evas_gl_common_shader_precompile_all(Evas_GL_Shared *shared)
{
Eina_List *li = evas_gl_common_shader_precompile_list(shared);
Evas_GL_Program *p;
int total, cnt = 0;
void *data;
total = eina_list_count(li);
EINA_LIST_FREE(li, data)
{
p = evas_gl_common_shader_generate_and_compile(shared, I(data));
if (p)
{
p->delete_me = 1;
cnt++;
}
}
DBG("Precompiled %d/%d shaders!", cnt, total);
return cnt;
}
int
evas_gl_common_shader_program_init(Evas_GL_Shared *shared)
{
/* most popular shaders */
const int BGRA = (shared->info.bgra ? SHADER_FLAG_BGRA : 0);
unsigned int autoload[] = {
/* rect */ 0,
/* text */ 0 | SHADER_FLAG_TEX | SHADER_FLAG_ALPHA,
/* img1 */ 0 | SHADER_FLAG_TEX | SHADER_FLAG_IMG | BGRA,
/* img2 */ 0 | SHADER_FLAG_TEX | SHADER_FLAG_IMG | SHADER_FLAG_NOMUL | BGRA,
};
Evas_GL_Program *p;
unsigned i;
if (getenv("EVAS_GL_RENDER_DISABLE_DITHER"))
_do_dither = EINA_FALSE;
if (_do_dither)
{
autoload[0] |= BASEFLAG;
autoload[1] |= BASEFLAG;
autoload[2] |= BASEFLAG;
autoload[3] |= BASEFLAG;
}
shared->shaders_hash = eina_hash_int32_new(_shaders_hash_free_cb);
if (_evas_gl_common_shader_binary_init(shared))
{
for (i = 0; i < (sizeof(autoload) / sizeof(autoload[0])); i++)
{
p = _evas_gl_common_shader_program_binary_load(shared->shaders_cache, autoload[i]);
if (p)
{
evas_gl_common_shader_textures_bind(p, EINA_TRUE);
eina_hash_add(shared->shaders_hash, &autoload[i], p);
}
}
}
else
{
evas_gl_common_shader_precompile_all(shared);
for (i = 0; i < (sizeof(autoload) / sizeof(autoload[0])); i++)
{
p = eina_hash_find(shared->shaders_hash, &autoload[i]);
if (p) p->delete_me = 0;
}
evas_gl_common_shaders_flush(shared);
}
return 1;
}
EMODAPI void
evas_gl_common_shaders_flush(Evas_GL_Shared *shared)
{
if (!shared) return;
if (!compiler_released)
{
compiler_released = EINA_TRUE;
#ifdef GL_GLES
glReleaseShaderCompiler();
#else
if (glsym_glReleaseShaderCompiler)
glsym_glReleaseShaderCompiler();
#endif
}
if (shared->needs_shaders_flush)
{
Eina_List *to_delete = NULL;
Eina_Iterator *it;
Evas_GL_Program *p;
_evas_gl_common_shader_binary_save(shared);
it = eina_hash_iterator_data_new(shared->shaders_hash);
EINA_ITERATOR_FOREACH(it, p)
{
if (p->delete_me)
to_delete = eina_list_append(to_delete, p);
}
eina_iterator_free(it);
EINA_LIST_FREE(to_delete, p)
eina_hash_del(shared->shaders_hash, &p->flags, p);
}
}
void
evas_gl_common_shader_program_shutdown(Evas_GL_Shared *shared)
{
if (!shared) return;
if (shared->needs_shaders_flush)
evas_gl_common_shaders_flush(shared);
if (shared->shaders_cache)
{
eet_close(shared->shaders_cache);
shared->shaders_cache = NULL;
eet_shutdown();
}
eina_hash_free(shared->shaders_hash);
shared->shaders_hash = NULL;
}
static inline unsigned int
evas_gl_common_shader_flags_get(Evas_GL_Shared *shared, Shader_Type type,
RGBA_Map_Point *map_points, int npoints,
int r, int g, int b, int a,
int sw, int sh, int w, int h, Eina_Bool smooth,
Evas_GL_Texture *tex, Eina_Bool tex_only,
Evas_GL_Texture *mtex, Eina_Bool mask_smooth,
Eina_Bool mask_color, int mw, int mh,
Eina_Bool alphaonly,
Shader_Sampling *psam, int *pnomul, Shader_Sampling *pmasksam)
{
Shader_Sampling sam = SHD_SAM11, masksam = SHD_SAM11;
int nomul = 1, bgra = 0, k;
unsigned int flags = 0;
if (_do_dither) flags |= BASEFLAG;
// image downscale sampling
if (smooth && ((type == SHD_IMAGE) || (type == SHD_IMAGENATIVE)))
{
if ((sw >= (w * 2)) && (sh >= (h * 2)))
sam = SHD_SAM22;
else if (sw >= (w * 2))
sam = SHD_SAM21;
else if (sh >= (h * 2))
sam = SHD_SAM12;
if (sam)
flags |= (1 << (SHADER_FLAG_SAM_BITSHIFT + sam - 1));
}
// mask downscale sampling
if (mtex && mask_smooth)
{
if ((mtex->w >= (mw * 2)) && (mtex->h >= (mh * 2)))
masksam = SHD_SAM22;
else if (mtex->w >= (mw * 2))
masksam = SHD_SAM21;
else if (mtex->h >= (mh * 2))
masksam = SHD_SAM12;
if (masksam)
flags |= (1 << (SHADER_FLAG_MASKSAM_BITSHIFT + masksam - 1));
}
// mask color mode
if (mtex && mask_color)
{
flags |= SHADER_FLAG_MASK_COLOR;
}
switch (type)
{
case SHD_RECT:
case SHD_LINE:
goto end;
case SHD_FONT:
flags |= (SHADER_FLAG_ALPHA | SHADER_FLAG_TEX);
goto end;
case SHD_IMAGE:
flags |= SHADER_FLAG_IMG;
break;
case SHD_IMAGENATIVE:
break;
case SHD_YUV:
flags |= SHADER_FLAG_YUV;
break;
case SHD_YUY2:
flags |= SHADER_FLAG_YUY2;
break;
case SHD_NV12:
flags |= SHADER_FLAG_NV12;
break;
case SHD_YUV_709:
flags |= (SHADER_FLAG_YUV_709 | SHADER_FLAG_YUV);
break;
case SHD_RGB_A_PAIR:
case SHD_MAP:
break;
case SHD_FILTER_DISPLACE:
flags |= SHADER_FLAG_FILTER_DISPLACE;
break;
case SHD_FILTER_CURVE:
flags |= SHADER_FLAG_FILTER_CURVE;
break;
case SHD_FILTER_BLUR_X:
flags |= SHADER_FLAG_FILTER_BLUR;
break;
case SHD_FILTER_BLUR_Y:
flags |= SHADER_FLAG_FILTER_BLUR;
flags |= SHADER_FLAG_FILTER_DIR_Y;
break;
case SHD_FILTER_GRAYSCALE:
flags |= SHADER_FLAG_FILTER_GRAYSCALE;
break;
case SHD_FILTER_INVERSE_COLOR:
flags |= SHADER_FLAG_FILTER_INVERSE_COLOR;
break;
default:
CRI("Impossible shader type.");
return 0;
}
if (alphaonly)
flags |= SHADER_FLAG_FILTER_ALPHA_ONLY;
// color mul
if ((a == 255) && (r == 255) && (g == 255) && (b == 255))
{
if (map_points)
{
for (k = 0; k < npoints; k++)
if (map_points[k].col != 0xffffffff)
{
nomul = 0;
break;
}
}
}
else
nomul = 0;
if (nomul)
flags |= SHADER_FLAG_NOMUL;
// bgra
if (tex_only)
{
if (tex->im && tex->im->native.target == GL_TEXTURE_EXTERNAL_OES)
flags |= SHADER_FLAG_EXTERNAL;
else
bgra = 1;
}
else
bgra = shared->info.bgra;
if (tex)
{
flags |= SHADER_FLAG_TEX;
if (!tex->alpha && tex_only)
{
if ((flags & SHADER_FLAG_EXTERNAL) || tex->pt->dyn.img)
flags |= SHADER_FLAG_AFILL;
}
}
if (bgra)
flags |= SHADER_FLAG_BGRA;
end:
if (mtex)
flags |= SHADER_FLAG_MASK;
if (psam) *psam = sam;
if (pnomul) *pnomul = nomul;
if (pmasksam) *pmasksam = masksam;
return flags;
}
void
evas_gl_common_shader_textures_bind(Evas_GL_Program *p, Eina_Bool prog_recover)
{
struct {
const char *name;
int enabled;
} textures[] = {
{ "tex", 0 },
{ "texm", 0 },
{ "texa", 0 },
{ "texu", 0 },
{ "texv", 0 },
{ "texuv", 0 },
{ "tex_filter", 0 },
{ NULL, 0 }
};
Eina_Bool hastex = 0;
GLint loc;
int i;
if (!p || (p->tex_count > 0)) return;
if ((p->flags & SHADER_FLAG_TEX) != 0)
{
textures[0].enabled = 1;
hastex = 1;
}
if ((p->flags & SHADER_FLAG_MASK) != 0)
{
textures[1].enabled = 1;
hastex = 1;
}
if ((p->flags & SHADER_FLAG_RGB_A_PAIR) != 0)
{
textures[2].enabled = 1;
hastex = 1;
}
if (p->flags & SHADER_FLAG_YUV)
{
textures[3].enabled = 1;
textures[4].enabled = 1;
hastex = 1;
}
else if ((p->flags & SHADER_FLAG_NV12) || (p->flags & SHADER_FLAG_YUY2))
{
textures[5].enabled = 1;
hastex = 1;
}
if ((p->flags & SHADER_FLAG_FILTER_DISPLACE) ||
(p->flags & SHADER_FLAG_FILTER_CURVE) ||
(p->flags & SHADER_FLAG_FILTER_BLUR))
{
textures[6].enabled = 1;
hastex = 1;
}
if (hastex)
{
GLuint curr_prog = 0;
if (prog_recover) glGetIntegerv(GL_CURRENT_PROGRAM, (GLint *)&curr_prog);
glUseProgram(p->prog); // is this necessary??
for (i = 0; textures[i].name; i++)
{
if (!textures[i].enabled) continue;
loc = glGetUniformLocation(p->prog, textures[i].name);
if (loc < 0)
{
ERR("Couldn't find uniform '%s' (shader: %08x)",
textures[i].name, p->flags);
}
glUniform1i(loc, p->tex_count++);
}
if (prog_recover) glUseProgram(curr_prog);
}
}
Evas_GL_Program *
evas_gl_common_shader_program_get(Evas_Engine_GL_Context *gc,
Shader_Type type,
RGBA_Map_Point *map_points, int npoints,
int r, int g, int b, int a,
int sw, int sh, int w, int h, Eina_Bool smooth,
Evas_GL_Texture *tex, Eina_Bool tex_only,
Evas_GL_Texture *mtex, Eina_Bool mask_smooth,
Eina_Bool mask_color, int mw, int mh,
Eina_Bool alphaonly,
Shader_Sampling *psam, int *pnomul,
Shader_Sampling *pmasksam)
{
unsigned int flags;
Evas_GL_Program *p;
flags = evas_gl_common_shader_flags_get(gc->shared, type, map_points, npoints, r, g, b, a,
sw, sh, w, h, smooth, tex, tex_only,
mtex, mask_smooth, mask_color, mw, mh,
alphaonly, psam, pnomul, pmasksam);
p = eina_hash_find(gc->shared->shaders_hash, &flags);
if (!p)
{
_evas_gl_common_shader_binary_init(gc->shared);
if (gc->shared->shaders_cache)
{
char pname[32];
sprintf(pname, SHADER_PROG_NAME_FMT, flags);
p = _evas_gl_common_shader_program_binary_load(gc->shared->shaders_cache, flags);
if (p)
{
evas_gl_common_shader_textures_bind(p, EINA_TRUE);
eina_hash_add(gc->shared->shaders_hash, &flags, p);
goto end;
}
}
p = evas_gl_common_shader_generate_and_compile(gc->shared, flags);
if (!p) return NULL;
}
end:
if (p->hitcount < PROGRAM_HITCOUNT_MAX)
p->hitcount++;
return p;
}