Added support for accelerated transformations & #expand directive

This commit is contained in:
Jarcode
2019-09-11 15:24:07 -07:00
parent c591b87c7b
commit c4b348cf8b
13 changed files with 1677 additions and 1285 deletions

View File

@@ -4,7 +4,7 @@ set -e
GLAD_GEN="${1:-c}"
pushd glad
python -m glad --generator=${GLAD_GEN} --extensions=GL_EXT_framebuffer_multisample,GL_EXT_texture_filter_anisotropic --local-files --out-path=.
python -m glad --generator=${GLAD_GEN} --extensions=GL_EXT_framebuffer_multisample,GL_EXT_texture_filter_anisotropic,GL_NV_texture_barrier --local-files --out-path=.
popd
cp glad/*.h glava/
cp glad/glad.c glava/

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
/*
OpenGL loader generated by glad 0.1.24a0 on Mon Aug 26 20:23:32 2019.
OpenGL loader generated by glad 0.1.24a0 on Tue Sep 10 15:02:41 2019.
Language/Generator: C/C++
Specification: gl
@@ -8,15 +8,16 @@
Profile: compatibility
Extensions:
GL_EXT_framebuffer_multisample,
GL_EXT_texture_filter_anisotropic
GL_EXT_texture_filter_anisotropic,
GL_NV_texture_barrier
Loader: True
Local files: True
Omit khrplatform: False
Commandline:
--profile="compatibility" --api="gl=4.6" --generator="c" --spec="gl" --local-files --extensions="GL_EXT_framebuffer_multisample,GL_EXT_texture_filter_anisotropic"
--profile="compatibility" --api="gl=4.6" --generator="c" --spec="gl" --local-files --extensions="GL_EXT_framebuffer_multisample,GL_EXT_texture_filter_anisotropic,GL_NV_texture_barrier"
Online:
http://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D4.6&extensions=GL_EXT_framebuffer_multisample&extensions=GL_EXT_texture_filter_anisotropic
http://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D4.6&extensions=GL_EXT_framebuffer_multisample&extensions=GL_EXT_texture_filter_anisotropic&extensions=GL_NV_texture_barrier
*/
@@ -5167,6 +5168,13 @@ GLAPI PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glad_glRenderbufferStorageMulti
#define GL_EXT_texture_filter_anisotropic 1
GLAPI int GLAD_GL_EXT_texture_filter_anisotropic;
#endif
#ifndef GL_NV_texture_barrier
#define GL_NV_texture_barrier 1
GLAPI int GLAD_GL_NV_texture_barrier;
typedef void (APIENTRYP PFNGLTEXTUREBARRIERNVPROC)(void);
GLAPI PFNGLTEXTUREBARRIERNVPROC glad_glTextureBarrierNV;
#define glTextureBarrierNV glad_glTextureBarrierNV
#endif
#ifdef __cplusplus
}

View File

@@ -200,9 +200,6 @@ static const char* help_str =
"The REQUEST argument is evaluated identically to the \'#request\' preprocessor directive\n"
"in GLSL files.\n"
"\n"
"The DEFINE argument is appended to the associated file before it is processed. It is\n"
"evaluated identically to the \'#define' preprocessor directive.\n"
"\n"
"The FILE argument may be any file path. All specified file paths are relative to the\n"
"active configuration root (usually ~/.config/glava if present).\n"
"\n"
@@ -243,7 +240,8 @@ static struct option p_opts[] = {
})
/* Wait for glava_renderer target texture to be initialized and valid */
__attribute__((visibility("default"))) void glava_wait(glava_handle* ref) {
__attribute__((visibility("default")))
void glava_wait(glava_handle* ref) {
while(__atomic_load_n(ref, __ATOMIC_SEQ_CST) == NULL) {
/* Edge case: handle has not been assigned */
struct timespec tv = {
@@ -257,25 +255,29 @@ __attribute__((visibility("default"))) void glava_wait(glava_handle* ref) {
pthread_mutex_unlock(&(*ref)->lock);
}
__attribute__((visibility("default"))) unsigned int glava_tex(glava_handle r) {
__attribute__((visibility("default")))
unsigned int glava_tex(glava_handle r) {
return r->off_tex;
}
/* Atomic size request */
__attribute__((visibility("default"))) void glava_sizereq(glava_handle r, int x, int y, int w, int h) {
__attribute__((visibility("default")))
void glava_sizereq(glava_handle r, int x, int y, int w, int h) {
r->sizereq = (typeof(r->sizereq)) { .x = x, .y = y, .w = w, .h = h };
__atomic_store_n(&r->sizereq_flag, GLAVA_REQ_RESIZE, __ATOMIC_SEQ_CST);
}
/* Atomic terminate request */
__attribute__((visibility("default"))) void glava_terminate(glava_handle* ref) {
__attribute__((visibility("default")))
void glava_terminate(glava_handle* ref) {
glava_handle store = __atomic_exchange_n(ref, NULL, __ATOMIC_SEQ_CST);
if (store)
__atomic_store_n(&store->alive, false, __ATOMIC_SEQ_CST);
}
/* Atomic reload request */
__attribute__((visibility("default"))) void glava_reload(glava_handle* ref) {
__attribute__((visibility("default")))
void glava_reload(glava_handle* ref) {
glava_handle store = __atomic_exchange_n(ref, NULL, __ATOMIC_SEQ_CST);
if (store) {
__atomic_store_n(&reload, true, __ATOMIC_SEQ_CST);
@@ -285,7 +287,8 @@ __attribute__((visibility("default"))) void glava_reload(glava_handle* ref) {
/* Main entry */
__attribute__((visibility("default"))) void glava_entry(int argc, char** argv, glava_handle* ret) {
__attribute__((visibility("default")))
void glava_entry(int argc, char** argv, glava_handle* ret) {
/* Evaluate these macros only once, since they allocate */
const char

View File

@@ -13,6 +13,7 @@
#include <fcntl.h>
#include <unistd.h>
#include "glava.h"
#include "render.h"
#include "glsl_ext.h"
@@ -24,6 +25,7 @@
#define COLOR 5
#define DEFINE 6
#define BIND 7
#define EXPAND 8
struct sbuf {
char* buf;
@@ -61,7 +63,7 @@ static void se_append(struct sbuf* sbuf, size_t elen, const char* fmt, ...) {
va_start(args, fmt);
int written;
if ((written = vsnprintf(sbuf->buf + sbuf->at, space, fmt, args)) < 0)
abort();
glava_abort();
sbuf->at += written;
va_end(args);
}
@@ -69,13 +71,13 @@ static void se_append(struct sbuf* sbuf, size_t elen, const char* fmt, ...) {
#define parse_error(line, f, fmt, ...) \
do { \
fprintf(stderr, "[%s:%d] " fmt "\n", f, (int) line, __VA_ARGS__); \
abort(); \
glava_abort(); \
} while (0)
#define parse_error_s(line, f, s) \
do { \
fprintf(stderr, "[%s:%d] " s "\n", f, (int) line); \
abort(); \
glava_abort(); \
} while (0)
struct schar {
@@ -149,7 +151,8 @@ static struct schar directive(struct glsl_ext* ext, char** args,
size_t bsz = (strlen(args[0]) * 3) + 64;
struct schar ret = { .buf = malloc(bsz) };
int r = snprintf(ret.buf, bsz, "#ifdef %1$s\n#undef %1$s\n#endif\n", args[0]);
if (r < 0) abort();
if (r < 0)
glava_abort();
ret.sz = r;
free_after(ext, ret.buf);
return ret;
@@ -180,21 +183,19 @@ static struct schar directive(struct glsl_ext* ext, char** args,
snprintf(path, sizeof(path) / sizeof(char), "%s/%s", ext->cd, target);
int fd = open(path, O_RDONLY);
if (fd == -1) {
if (fd == -1)
parse_error(line, f, "failed to load GLSL shader source "
"specified by #include directive '%s': %s\n",
path, strerror(errno));
}
struct stat st;
fstat(fd, &st);
char* map = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
if (!map) {
if (!map)
parse_error(line, f, "failed to map GLSL shader source "
"specified by #include directive '%s': %s\n",
path, strerror(errno));
}
struct glsl_ext next = {
.source = map,
@@ -205,7 +206,8 @@ static struct schar directive(struct glsl_ext* ext, char** args,
.handlers = ext->handlers,
.binds = ext->binds,
.ss_lookup = ext->ss_lookup,
.ss_len = ext->ss_len
.ss_len = ext->ss_len,
.efuncs = ext->efuncs
};
/* recursively process */
@@ -238,11 +240,10 @@ static struct schar directive(struct glsl_ext* ext, char** args,
char c;
size_t i;
for (i = 0; (c = handler->fmt[i]) != '\0'; ++i) {
if (args_sz <= 1 + i) {
if (args_sz <= 1 + i)
parse_error(line, f,
"failed to execute request '%s': expected format '%s'\n",
request, handler->fmt);
}
char* raw = args[1 + i];
switch (c) {
case 'i': {
@@ -271,13 +272,12 @@ static struct schar directive(struct glsl_ext* ext, char** args,
case '1': { v = true; break; }
case '0': { v = false; break; }
default:
parse_error_s(line, f,
"tried to parse invalid raw string into a boolean");
}
} else {
parse_error_s(line, f,
"tried to parse invalid raw string into a boolean");
parse_error_s(line, f, "tried to parse invalid "
"raw string into a boolean");
}
} else
parse_error_s(line, f, "tried to parse invalid "
"raw string into a boolean");
processed_args[i] = malloc(sizeof(bool));
*(bool*) processed_args[i] = v;
break;
@@ -293,11 +293,51 @@ static struct schar directive(struct glsl_ext* ext, char** args,
free(processed_args);
}
}
if (!found) {
if (!found)
parse_error(line, f, "unknown request type '%s'", request);
}
goto return_empty;
}
case EXPAND: {
if (args_sz >= 2) {
char* fmacro = args[0];
size_t fmacro_sz = strlen(fmacro);
char* arg = args[1];
size_t expand_n = 0;
bool match = false;
if (ext->efuncs) {
for (size_t t = 0; ext->efuncs[t].name != NULL; ++t) {
if (!strcmp(arg, ext->efuncs[t].name)) {
expand_n = ext->efuncs[t].call();
match = true;
break;
}
}
}
if (!match)
parse_error(line, f, "#expand directive specified invalid input \"%s\"", arg);
/* (2 {paren} + 1 {semicolon} + 1 {newline} + 4 {input buf} + macro) * expand + 1 */
size_t bsz = ((8 + fmacro_sz) * expand_n) + 1;
struct schar ret = { .buf = malloc(bsz) };
int r = 0;
for (size_t t = 0; t < expand_n; ++t) {
int sr = snprintf(ret.buf + r, bsz - r, "%s(%d);\n", args[0], (int) t);
if (sr >= 0)
r += sr;
else
parse_error(line, f, "internal formatting error (snprintf returned %d)", sr);
}
ret.sz = r;
free_after(ext, ret.buf);
return ret;
} else
parse_error(line, f, "#expand directive missing arguments, "
"requires 2 identifiers (got %d)\n", (int) args_sz);
goto return_empty;
}
return_empty:
default: return (struct schar) { .buf = NULL, .sz = 0 };
}
}
@@ -339,7 +379,7 @@ void ext_process(struct glsl_ext* ext, const char* f) {
size_t args_sz = 0;
bool prev_slash = false, comment = false, comment_line = false, prev_asterix = false,
prev_escape = false, string = false;
prev_escape = false, string = false, skip_color_start = false;
se_append(&sbuf, 32, "#line 1 %d\n", ss_cur);
@@ -406,11 +446,18 @@ void ext_process(struct glsl_ext* ext, const char* f) {
goto copy;
case '#': {
/* handle hex color syntax */
if (!comment && !string) {
if (!comment && !string && !skip_color_start) {
if (ext->source[t + 1] == '#') {
skip_color_start = true;
goto normal_char;
}
state = COLOR;
cbuf_idx = 0;
continue;
} else goto normal_char;
} else {
skip_color_start = false;
goto normal_char;
}
}
case '@': {
/* handle bind syntax */
@@ -534,7 +581,8 @@ void ext_process(struct glsl_ext* ext, const char* f) {
/* To emit the default, we push back the cursor to where it starts
and simply resume parsing from a normal context. */
t = b_restart;
} else parse_error(line, f, "Unexpected `--pipe` binding name '@%s' while parsing GLSL."
} else parse_error(line, f,
"Unexpected `--pipe` binding name '@%s' while parsing GLSL."
" Try assigning a default or binding the value.", parsed_name);
}
state = GLSL;
@@ -566,6 +614,7 @@ void ext_process(struct glsl_ext* ext, const char* f) {
DIRECTIVE_CASE(request, REQUEST);
DIRECTIVE_CASE(include, INCLUDE);
DIRECTIVE_CASE(define, DEFINE);
DIRECTIVE_CASE(expand, EXPAND);
/* no match */
if (state == MACRO) skip_macro();
@@ -579,10 +628,15 @@ void ext_process(struct glsl_ext* ext, const char* f) {
*args = NULL;
}
}
case '0' ... '9':
/* digits at the start of an identifier are not legal */
if (macro_start_idx == t - 1)
goto macro_parse_error;
case 'a' ... 'z':
case 'A' ... 'Z':
continue;
default:
macro_parse_error:
/* invalid char, malformed! */
parse_error(line, f, "Unexpected character '%c' while parsing GLSL directive", at);
}
@@ -598,7 +652,8 @@ void ext_process(struct glsl_ext* ext, const char* f) {
} } while (0)
case REQUEST:
case INCLUDE:
case DEFINE: {
case DEFINE:
case EXPAND: {
switch (at) {
case ' ':
case '\t':

View File

@@ -28,6 +28,17 @@ struct request_handler {
#endif
};
struct glsl_ext_efunc {
char* name;
#if defined(__clang__)
size_t (^call)(void);
#elif defined(__GNUC__) || defined(__GNUG__)
size_t (*call)(void);
#else
#error "no nested function/block syntax available"
#endif
};
struct glsl_ext {
char* processed; /* OUT: null terminated processed source */
size_t p_len; /* OUT: length of processed buffer, excluding null char */
@@ -37,6 +48,7 @@ struct glsl_ext {
const char* cfd; /* IN: config directory, if NULL it is assumed to cd */
const char* dd; /* IN: default directory */
struct rd_bind* binds; /* OPT IN: --pipe binds */
struct glsl_ext_efunc* efuncs; /* OPT IN: `#expand` binds */
void** destruct; /* internal */
size_t destruct_sz; /* internal */
char** ss_lookup; /* source-string lookup table */

View File

@@ -32,6 +32,34 @@ typeof(bind_types) bind_types = {
{}
};
/* The following macros are some black magic that allow the use of
by-reference C closures in both Clang and GCC. Their behaviour
differs slightly so please read up on both nested GCC functions
and Clang blocks if you are going to use these. */
#if defined(__clang__)
#define MUTABLE __block
#define INLINE(t, x) MUTABLE __auto_type x = ^t
#else
#define MUTABLE
#define INLINE(t, x) t x
#endif
#if defined(__clang__)
static void* block_storage;
#define RHANDLER(name, args, ...) \
({ block_storage = ^(const char* name, void** args) __VA_ARGS__; \
(typeof(^(const char* name, void** args) __VA_ARGS__)) block_storage; })
#define CLOSURE(ret, ...) ({ block_storage = ^ret __VA_ARGS__; \
(typeof(^ret __VA_ARGS__)) block_storage; })
#elif defined(__GNUC__) || defined(__GNUG__)
#define RHANDLER(name, args, ...) \
({ void _handler(const char* name, void** args) __VA_ARGS__ _handler; })
#define CLOSURE(ret, ...) ({ ret _handler __VA_ARGS__; _handler; })
#else
#error "no nested function/block syntax available"
#endif
#define TWOPI 6.28318530718
#define PI 3.14159265359
#define swap(a, b) do { __auto_type tmp = a; a = b; b = tmp; } while (0)
@@ -43,11 +71,16 @@ typeof(bind_types) bind_types = {
#define IB_WORK_LEFT 4
#define IB_WORK_RIGHT 5
/* Only a single vertex shader is needed for GLava, since all rendering is done in the fragment shader
over a fullscreen quad */
/* Only a single vertex shader is needed, since all rendering
is done in the fragment shader over a fullscreen quad */
#define VERTEX_SHADER_SRC \
"layout(location = 0) in vec3 pos; void main() { gl_Position = vec4(pos.x, pos.y, 0.0F, 1.0F); }"
/* Should be defined from meson */
#ifndef GLAVA_RESOURCE_PATH
#define GLAVA_RESOURCE_PATH "../resources/"
#endif
bool glad_instantiated = false;
struct gl_wcb* wcbs[2] = {};
static size_t wcbs_idx = 0;
@@ -68,27 +101,37 @@ struct gl_bind_src {
int src_type;
};
/* function that can be applied to uniform binds */
/* Function that can be applied to uniform binds */
struct gl_transform {
const char* name;
int type;
void (*apply)(struct gl_data*, void**, void* data);
bool opt; /* true if the transform is a post-FFT transformation */
};
/* data for sampler1D */
/* Data for sampler1D */
struct gl_sampler_data {
float* buf;
size_t sz;
};
/* per-bind data containing the framebuffer and 1D texture to render for smoothing. */
/* Per-bind data containing the framebuffer and 1D texture to render
for smoothing or averaging output */
struct sm_fb {
GLuint fbo, tex;
};
/* Per-bind data containing the framebuffer and textures gravity output.
There are multiple output framebuffers for GLSL frame averaging */
struct gr_fb {
struct sm_fb* out;
size_t out_sz;
size_t out_idx;
};
/* GLSL uniform bind */
struct gl_bind {
@@ -98,7 +141,9 @@ struct gl_bind {
int src_type;
void (**transformations)(struct gl_data*, void**, void* data);
size_t t_sz;
struct sm_fb sm;
struct sm_fb sm, av, gr_store;
struct gr_fb gr;
bool optimize_fft;
};
/* GL screen framebuffer object */
@@ -121,7 +166,7 @@ struct overlay_data {
struct gl_data {
struct gl_sfbo* stages;
struct overlay_data overlay;
GLuint audio_tex_r, audio_tex_l, bg_tex, sm_prog;
GLuint audio_tex_r, audio_tex_l, bg_tex, sm_prog, av_prog, gr_prog, p_prog;
size_t stages_sz, bufscale, avg_frames;
void* w;
struct gl_wcb* wcb;
@@ -130,9 +175,9 @@ struct gl_data {
double tcounter;
float time, timecycle;
int fcounter, ucounter, kcounter;
bool print_fps, avg_window, interpolate, force_geometry, force_raised,
copy_desktop, smooth_pass, premultiply_alpha, check_fullscreen,
clickthrough, mirror_input;
bool print_fps, avg_window, interpolate, interpolate_glsl, force_geometry,
force_raised, copy_desktop, smooth_pass, premultiply_alpha, check_fullscreen,
clickthrough, mirror_input, accel_fft;
void** t_data;
size_t t_count;
float gravity_step, target_spu, fr, ur, smooth_distance, smooth_ratio,
@@ -146,8 +191,10 @@ struct gl_data {
struct rd_bind* binds;
GLuint bg_prog, bg_utex, bg_screen;
bool bg_setup;
GLuint sm_utex, sm_usz, sm_uw;
bool sm_setup;
GLuint sm_utex, sm_usz, sm_uw,
gr_utex, gr_udiff,
p_utex;
GLuint* av_utex;
bool test_mode;
struct gl_sfbo off_sfbo;
#ifdef GLAVA_DEBUG
@@ -178,7 +225,7 @@ static GLuint shaderload(const char* rpath,
size_t s_len = strlen(shader);
/* Path buffer, used for output and */
/* Path buffer for error message mapping */
char path[raw ? 2 : strlen(rpath) + s_len + 2];
if (raw) {
path[0] = '*';
@@ -186,10 +233,8 @@ static GLuint shaderload(const char* rpath,
}
struct stat st;
int fd = -1;
if (!raw) {
snprintf(path, sizeof(path) / sizeof(char), "%s/%s", shader, rpath);
fd = open(path, O_RDONLY);
if (fd == -1) {
fprintf(stderr, "failed to load shader '%s': %s\n", path, strerror(errno));
@@ -198,7 +243,6 @@ static GLuint shaderload(const char* rpath,
fstat(fd, &st);
}
/* open and create a copy with prepended header */
GLint max_uniforms;
glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &max_uniforms);
@@ -210,6 +254,7 @@ static GLuint shaderload(const char* rpath,
const char* fmt = "uniform %s _IN_%s;\n";
/* Construct pipe binding header (containing uniforms) */
for (struct rd_bind* bd = gl->binds; bd->name != NULL; ++bd) {
size_t inc = snprintf(NULL, 0, fmt, bd->stype, bd->name);
bind_header = realloc(bind_header, bh_idx + inc + 1);
@@ -217,19 +262,38 @@ static GLuint shaderload(const char* rpath,
bh_idx += inc;
}
static const GLchar* header_fmt =
"#version %d\n"
"#define _UNIFORM_LIMIT %d\n"
"#define _PRE_SMOOTHED_AUDIO %d\n"
"#define _SMOOTH_FACTOR %.6f\n"
"#define _USE_ALPHA %d\n"
"#define _PREMULTIPLY_ALPHA %d\n"
"#define _CHANNELS %d\n"
"#define USE_STDIN %d\n"
"#if USE_STDIN == 1\n"
"uniform %s STDIN;\n"
"#endif\n"
"%s";
/* Append to header entries with a #define for each `#expand` control */
MUTABLE char* efmt_header = malloc(1);
MUTABLE size_t efmt_idx = 0;
INLINE(void, append_efmt)(const char* n, size_t v) {
size_t inc = snprintf(NULL, 0, "#define %s %d\n", n, (int) v);
efmt_header = realloc(efmt_header, efmt_idx + inc + 1);
snprintf(efmt_header + efmt_idx, inc + 1, "#define %s %d\n", n, (int) v);
efmt_idx += inc;
};
/* Create `#expand` header entry, using the above closure */
#define EBIND(n, v) \
({ \
struct glsl_ext_efunc ret = \
{ .name = n, .call = CLOSURE(size_t, (void) { return v; })}; \
append_efmt(n, v); \
ret; \
})
struct glsl_ext_efunc efuncs[] = {
EBIND("_AVG_FRAMES", gl->avg_frames ),
EBIND("_AVG_WINDOW", (int) gl->avg_window ),
EBIND("_USE_ALPHA", 1 ),
EBIND("_PREMULTIPLY_ALPHA", gl->premultiply_alpha ? 1 : 0),
EBIND("_CHANNELS", gl->mirror_input ? 1 : 2 ),
EBIND("_UNIFORM_LIMIT", (int) max_uniforms ),
EBIND("_PRE_SMOOTHED_AUDIO", gl->smooth_pass ? 1 : 0 ),
{ .name = NULL }
};
#undef EBIND
size_t pad = bh_idx + efmt_idx;
struct glsl_ext ext = {
.source = raw ? NULL : map,
@@ -240,19 +304,27 @@ static GLuint shaderload(const char* rpath,
.handlers = handlers,
.processed = (char*) (raw ? shader : NULL),
.p_len = raw ? s_len : 0,
.binds = gl->binds
.binds = gl->binds,
.efuncs = efuncs
};
/* If this is raw input, skip processing */
if (!raw) ext_process(&ext, rpath);
size_t blen = strlen(header_fmt) + 64;
/* Format GLSL header with defines, pipe bindings, and expand constants. */
static const GLchar* header_fmt =
"#version %d\n"
"#define _SMOOTH_FACTOR %.6f\n"
"#define USE_STDIN %d\n"
"#if USE_STDIN == 1\n"
"uniform %s STDIN;\n"
"#endif\n" "%s\n" "%s";
size_t blen = strlen(header_fmt) + 32 + pad;
GLchar* buf = malloc((blen * sizeof(GLchar*)) + ext.p_len);
int written = snprintf(buf, blen, header_fmt, (int) shader_version, (int) max_uniforms,
gl->smooth_pass ? 1 : 0, (double) gl->smooth_factor,
1, gl->premultiply_alpha ? 1 : 0, gl->mirror_input ? 1 : 2,
gl->stdin_type != STDIN_TYPE_NONE, bind_types[gl->stdin_type].n,
bind_header);
int written = snprintf(buf, blen, header_fmt, (int) shader_version,
(double) gl->smooth_factor, gl->stdin_type != STDIN_TYPE_NONE,
bind_types[gl->stdin_type].n, bind_header, efmt_header);
if (written < 0) {
fprintf(stderr, "snprintf() encoding error while prepending header to shader '%s'\n", path);
return 0;
@@ -266,11 +338,11 @@ static GLuint shaderload(const char* rpath,
switch (glGetError()) {
case GL_INVALID_VALUE:
fprintf(stderr, "invalid value while loading shader source\n");
abort(); //todo: remove
glava_abort();
return 0;
case GL_INVALID_OPERATION:
fprintf(stderr, "invalid operation while loading shader source\n");
abort(); //todo: remove
glava_abort();
return 0;
default: {}
}
@@ -418,11 +490,6 @@ static GLuint shaderbuild_f(struct gl_data* gl,
return 0;
}
} else if (!strcmp(path + t + 1, "vert")) {
/*
if (!(shaders[i] = shaderload(path, GL_VERTEX_SHADER, shader_path))) {
return 0;
}
*/
fprintf(stderr, "shaderbuild(): vertex shaders not allowed: %s\n", path);
abort();
} else {
@@ -434,7 +501,8 @@ static GLuint shaderbuild_f(struct gl_data* gl,
}
}
/* load builtin vertex shader */
shaders[sz] = shaderload(NULL, GL_VERTEX_SHADER, VERTEX_SHADER_SRC, NULL, NULL, handlers, shader_version, true, NULL, gl);
shaders[sz] = shaderload(NULL, GL_VERTEX_SHADER, VERTEX_SHADER_SRC,
NULL, NULL, handlers, shader_version, true, NULL, gl);
fflush(stdout);
return shaderlink_f(shaders);
}
@@ -557,8 +625,7 @@ static void glad_debugcb(const char* name, void *funcptr, int len_args, ...) {
if (err != GL_NO_ERROR) {
const char* cname = "?", * msg = "Unknown error code";
size_t t;
for (t = 0; t < sizeof(err_lookup) / sizeof(struct err_msg); ++t) {
for (size_t t = 0; t < sizeof(err_lookup) / sizeof(struct err_msg); ++t) {
if (err_lookup[t].code == err) {
cname = err_lookup[t].cname;
msg = err_lookup[t].msg;
@@ -590,7 +657,7 @@ static struct gl_bind_src bind_sources[] = {
{ .name = "time", .type = BIND_FLOAT, .src_type = SRC_SCREEN }
};
#define window(t, sz) (0.53836 - (0.46164 * cos(TWOPI * (double) t / (double)(sz - 1))))
#define window(t, sz) (0.53836 - (0.46164 * cos(TWOPI * (double) t / (double)sz)))
#define ALLOC_ONCE(u, udata, sz) \
if (*udata == NULL) { \
u = calloc(sz, sizeof(typeof(*u))); \
@@ -636,7 +703,7 @@ void transform_smooth(struct gl_data* d, void** _, void* data) {
/* Calculate real indexes for sampling at this position, since the
distance is specified in scalar values */
int smin = (int) floor(powf(E, max(db - d->smooth_distance, 0)));
int smax = min((int) ceil(powf(E, db + d->smooth_distance)), sz - 1);
int smax = min((int) ceil(powf(E, db + d->smooth_distance)), (int) sz - 1);
int count = 0;
for (int s = smin; s <= smax; ++s) {
if (b[s]) {
@@ -679,6 +746,7 @@ void transform_average(struct gl_data* d, void** udata, void* data) {
float* bufs;
ALLOC_ONCE(bufs, udata, tsz);
/* TODO: optimize into circle buffer */
memmove(bufs, &bufs[sz], (tsz - sz) * sizeof(float));
memcpy(&bufs[tsz - sz], b, sz * sizeof(float));
@@ -694,7 +762,7 @@ void transform_average(struct gl_data* d, void** udata, void* data) {
} while (0)
if (use_window)
DO_AVG(window(f, d->avg_frames));
DO_AVG(window(f, d->avg_frames - 1));
else
DO_AVG(1);
@@ -711,16 +779,6 @@ void transform_wrange(struct gl_data* d, void** _, void* data) {
}
}
void transform_window(struct gl_data* d, void** _, void* data) {
struct gl_sampler_data* s = (struct gl_sampler_data*) data;
float* b = s->buf;
size_t sz = s->sz, t;
for (t = 0; t < sz; ++t) {
b[t] *= window(t, sz);
}
}
void transform_fft(struct gl_data* d, void** _, void* in) {
struct gl_sampler_data* s = (struct gl_sampler_data*) in;
float* data = s->buf;
@@ -730,6 +788,11 @@ void transform_fft(struct gl_data* d, void** _, void* in) {
float wtemp, wr, wpr, wpi, wi, theta;
float tempr, tempi;
/* apply window */
for (i = 0; i < s->sz; ++i) {
data[i] *= window(i, s->sz - 1);
}
/* reverse-binary reindexing */
n = nn << 1;
j = 1;
@@ -783,17 +846,16 @@ void transform_fft(struct gl_data* d, void** _, void* in) {
}
static struct gl_transform transform_functions[] = {
{ .name = "window", .type = BIND_SAMPLER1D, .apply = transform_window },
{ .name = "window", .type = BIND_SAMPLER1D, .apply = NULL },
{ .name = "fft", .type = BIND_SAMPLER1D, .apply = transform_fft },
{ .name = "wrange", .type = BIND_SAMPLER1D, .apply = transform_wrange },
{ .name = "avg", .type = BIND_SAMPLER1D, .apply = transform_average },
{ .name = "gravity", .type = BIND_SAMPLER1D, .apply = transform_gravity },
{ .name = "avg", .type = BIND_SAMPLER1D, .apply = NULL },
{ .name = "gravity", .type = BIND_SAMPLER1D, .apply = NULL },
{ .name = "smooth", .type = BIND_SAMPLER1D, .apply = transform_smooth }
};
static struct gl_bind_src* lookup_bind_src(const char* str) {
size_t t;
for (t = 0; t < sizeof(bind_sources) / sizeof(struct gl_bind_src); ++t) {
for (size_t t = 0; t < sizeof(bind_sources) / sizeof(struct gl_bind_src); ++t) {
if (!strcmp(bind_sources[t].name, str)) {
return &bind_sources[t];
}
@@ -801,25 +863,6 @@ static struct gl_bind_src* lookup_bind_src(const char* str) {
return NULL;
}
#if defined(__clang__)
#define MUTABLE __block
#define INLINE(t, x) MUTABLE __auto_type x = ^t
#else
#define MUTABLE
#define INLINE(t, x) t x
#endif
#if defined(__clang__)
static void (^block_storage)(const char*, void**);
#define RHANDLER(name, args, ...) \
({ block_storage = ^(const char* name, void** args) __VA_ARGS__; block_storage; })
#elif defined(__GNUC__) || defined(__GNUG__)
#define RHANDLER(name, args, ...) \
({ void _handler(const char* name, void** args) __VA_ARGS__ _handler; })
#else
#error "no nested function/block syntax available"
#endif
struct glava_renderer* rd_new(const char** paths, const char* entry,
const char** requests, const char* force_backend,
struct rd_bind* bindings, int stdin_type,
@@ -866,6 +909,7 @@ struct glava_renderer* rd_new(const char** paths, const char* entry,
.avg_window = true,
.gravity_step = 4.2,
.interpolate = true,
.interpolate_glsl = false,
.force_geometry = false,
.force_raised = false,
.smooth_factor = 0.025,
@@ -873,20 +917,24 @@ struct glava_renderer* rd_new(const char** paths, const char* entry,
.smooth_ratio = 4,
.bg_tex = 0,
.sm_prog = 0,
.av_prog = 0,
.gr_prog = 0,
.p_prog = 0,
.copy_desktop = true,
.premultiply_alpha = true,
.mirror_input = false,
.accel_fft = true,
.check_fullscreen = false,
.smooth_pass = true,
.fft_scale = 10.2F,
.fft_cutoff = 0.3F,
.geometry = { 0, 0, 500, 400 },
.clear_color = { 0.0F, 0.0F, 0.0F, 0.0F },
.interpolate_buf = { [0] = NULL },
.clickthrough = false,
.stdin_type = stdin_type,
.binds = bindings,
.bg_setup = false,
.sm_setup = false,
.test_mode = test_mode,
.off_sfbo = {
.name = "test",
@@ -951,7 +999,7 @@ struct glava_renderer* rd_new(const char** paths, const char* entry,
if (!gl->wcb) {
fprintf(stderr, "Invalid window creation backend selected: '%s'\n", backend);
abort();
glava_abort();
}
#ifdef GLAD_DEBUG
@@ -1132,6 +1180,8 @@ struct glava_renderer* rd_new(const char** paths, const char* entry,
.handler = RHANDLER(name, args, { r->rate_request = *(int*) args[0]; }) },
{ .name = "setsamplesize", .fmt = "i",
.handler = RHANDLER(name, args, { r->samplesize_request = *(int*) args[0]; }) },
{ .name = "setaccelfft", .fmt = "b",
.handler = RHANDLER(name, args, { gl->accel_fft = *(bool*) args[0]; }) },
{ .name = "setavgframes", .fmt = "i",
.handler = RHANDLER(name, args, {
if (!loading_smooth_pass) gl->avg_frames = *(int*) args[0]; }) },
@@ -1203,6 +1253,27 @@ struct glava_renderer* rd_new(const char** paths, const char* entry,
realloc(bind->transformations, bind->t_sz * sizeof(void (*)(void*)));
bind->transformations[bind->t_sz - 1] = tran->apply;
++t_count;
/* Edge case (for backwards compatibility): gravity and average is implied
by fft, reserve storage pointers for these operations */
if (!strcmp(transform_functions[t].name, "fft")) {
t_count += 2;
}
static const char* fmt = "WARNING: using \"%s\" transform explicitly "
"is deprecated; implied from \"fft\" transform.\n";
if (!strcmp(transform_functions[t].name, "gravity")) {
static bool gravity_warn = false;
if (!gravity_warn) {
fprintf(stderr, fmt, transform_functions[t].name);
gravity_warn = true;
}
}
if (!strcmp(transform_functions[t].name, "avg")) {
static bool avg_warn = false;
if (!avg_warn) {
fprintf(stderr, fmt, transform_functions[t].name);
avg_warn = true;
}
}
})
},
{ .name = "uniform", .fmt = "ss",
@@ -1226,7 +1297,8 @@ struct glava_renderer* rd_new(const char** paths, const char* entry,
.src_type = src->src_type,
.transformations = malloc(1),
.t_sz = 0,
.sm = {}
.gr = { .out = NULL },
.optimize_fft = false
};
})
},
@@ -1464,7 +1536,8 @@ struct glava_renderer* rd_new(const char** paths, const char* entry,
current = s;
bool skip;
GLuint id = shaderbuild(gl, shaders, data, dd, handlers, shader_version, &skip, d->d_name);
GLuint id = shaderbuild(gl, shaders, data, dd,
handlers, shader_version, &skip, d->d_name);
if (skip && verbose) printf("disabled: '%s'\n", d->d_name);
/* check for compilation failure */
if (!id && !skip)
@@ -1540,7 +1613,7 @@ struct glava_renderer* rd_new(const char** paths, const char* entry,
if (final) final->indirect = false;
}
/* Compile smooth pass shader */
/* Compile various audio processing shaders */
{
const char* util_folder = "util";
@@ -1548,12 +1621,47 @@ struct glava_renderer* rd_new(const char** paths, const char* entry,
size_t usz = d_len + u_len + 2;
char util[usz]; /* module pack path to use */
snprintf(util, usz, "%s/%s", data, util_folder);
/* Compile smooth pass shader */
loading_smooth_pass = true;
if (!(gl->sm_prog = shaderbuild(gl, util, data, dd, handlers, shader_version, NULL, "smooth_pass.frag")))
abort();
if (!(gl->sm_prog = shaderbuild(gl, util, data, dd, handlers, shader_version,
NULL, "smooth_pass.frag")))
glava_abort();
gl->sm_utex = glGetUniformLocation(gl->sm_prog, "tex");
gl->sm_usz = glGetUniformLocation(gl->sm_prog, "sz");
gl->sm_uw = glGetUniformLocation(gl->sm_prog, "w");
glBindFragDataLocation(gl->sm_prog, 1, "fragment");
loading_smooth_pass = false;
if (gl->accel_fft) {
/* Compile gravity pass shader */
if (!(gl->gr_prog = shaderbuild(gl, util, data, dd, handlers, shader_version,
NULL, "gravity_pass.frag")))
glava_abort();
gl->gr_utex = glGetUniformLocation(gl->gr_prog, "tex");
gl->gr_udiff = glGetUniformLocation(gl->gr_prog, "diff");
/* Compile averaging shader */
if (!(gl->av_prog = shaderbuild(gl, util, data, dd, handlers, shader_version,
NULL, "average_pass.frag")))
glava_abort();
char buf[6];
gl->av_utex = malloc(sizeof(GLuint) * gl->avg_frames);
for (size_t t = 0; t < gl->avg_frames; ++t) {
snprintf(buf, sizeof(buf), "t%d", (int) t);
gl->av_utex[t] = glGetUniformLocation(gl->av_prog, buf);
}
/* Compile pass shader (straight 1D texture map) */
if (!(gl->p_prog = shaderbuild(gl, util, data, dd, handlers, shader_version,
NULL, "pass.frag")))
glava_abort();
gl->p_utex = glGetUniformLocation(gl->p_prog, "tex");
}
}
/* Compile averaging shader */
/* target seconds per update */
gl->target_spu = (float) (r->samplesize_request / 4) / (float) r->rate_request;
@@ -1573,14 +1681,11 @@ struct glava_renderer* rd_new(const char** paths, const char* entry,
gl->interpolate_buf[IB_WORK_RIGHT ] = &ibuf[isz * IB_WORK_RIGHT ]; /* right interpolation results */
}
{
gl->t_data = malloc(sizeof(void*) * t_count);
gl->t_count = t_count;
size_t t;
for (t = 0; t < t_count; ++t) {
for (size_t t = 0; t < t_count; ++t) {
gl->t_data[t] = NULL;
}
}
overlay(&gl->overlay);
@@ -1591,6 +1696,37 @@ struct glava_renderer* rd_new(const char** paths, const char* entry,
return r;
}
static void bind_1d_fbo(struct sm_fb* sm, size_t sz) {
if (sm->tex == 0) {
glGenTextures(1, &sm->tex);
glGenFramebuffers(1, &sm->fbo);
/* 1D texture parameters */
glBindTexture(GL_TEXTURE_1D, sm->tex);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexImage1D(GL_TEXTURE_1D, 0, GL_R16, sz, 0, GL_RED, GL_FLOAT, NULL);
/* setup and bind framebuffer to texture */
glBindFramebuffer(GL_FRAMEBUFFER, sm->fbo);
glFramebufferTexture1D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,\
GL_TEXTURE_1D, sm->tex, 0);
switch (glCheckFramebufferStatus(GL_FRAMEBUFFER)) {
case GL_FRAMEBUFFER_COMPLETE: break;
default:
fprintf(stderr, "error in frambuffer state\n");
glava_abort();
}
} else {
/* Just bind our data if it was already allocated and setup */
glBindFramebuffer(GL_FRAMEBUFFER, sm->fbo);
glBindTexture(GL_TEXTURE_1D, sm->tex);
}
}
void rd_time(struct glava_renderer* r) {
struct gl_data* gl = r->gl;
@@ -1616,7 +1752,7 @@ bool rd_update(struct glava_renderer* r, float* lb, float* rb, size_t bsz, bool
/* Force disable interpolation if the update rate is close to or higher than the frame rate */
float uratio = (gl->ur / gl->fr); /* update : framerate ratio */
bool old_interpolate = gl->interpolate;
MUTABLE bool old_interpolate = gl->interpolate;
gl->interpolate = uratio <= 0.9F ? old_interpolate : false;
/* Perform buffer scaling */
@@ -1647,10 +1783,10 @@ bool rd_update(struct glava_renderer* r, float* lb, float* rb, size_t bsz, bool
}
/* Linear interpolation */
float
* ilb = gl->interpolate_buf[IB_WORK_LEFT ],
* irb = gl->interpolate_buf[IB_WORK_RIGHT];
float * ilb = NULL, * irb = NULL;
if (gl->interpolate) {
ilb = gl->interpolate_buf[IB_WORK_LEFT ];
irb = gl->interpolate_buf[IB_WORK_RIGHT];
for (t = 0; t < bsz; ++t) {
/* Obtain start/end values at this index for left & right buffers */
float
@@ -1687,7 +1823,9 @@ bool rd_update(struct glava_renderer* r, float* lb, float* rb, size_t bsz, bool
}
/* Resize and grab new background data if needed */
if (gl->copy_desktop && (gl->wcb->bg_changed(gl->w) || ww != gl->lww || wh != gl->lwh || wx != gl->lwx || wy != gl->lwy)) {
if (gl->copy_desktop && (gl->wcb->bg_changed(gl->w)
|| ww != gl->lww || wh != gl->lwh
|| wx != gl->lwx || wy != gl->lwy)) {
gl->bg_tex = xwin_copyglbg(r, gl->bg_tex);
}
@@ -1883,7 +2021,8 @@ bool rd_update(struct glava_renderer* r, float* lb, float* rb, size_t bsz, bool
glClear(GL_COLOR_BUFFER_BIT);
if (!current->indirect && gl->copy_desktop) {
/* Shader to flip texture and override alpha channel */
/* Shader to flip texture and override alpha channel.
This is embedded since we don't need any GLSL preprocessing here */
static const char* frag_shader =
"uniform sampler2D tex;" "\n"
"uniform ivec2 screen;" "\n"
@@ -1964,74 +2103,173 @@ bool rd_update(struct glava_renderer* r, float* lb, float* rb, size_t bsz, bool
struct gl_bind* bind = &current->binds[b];
/* Handle transformations and bindings for 1D samplers */
INLINE(void, handle_1d_tex)(GLuint tex, float* buf, float* ubuf,
INLINE(void, handle_audio)(GLuint tex, float* buf, float* ubuf,
size_t sz, int offset, bool audio) {
if (load_flags[offset])
goto bind_uniform;
load_flags[offset] = true;
/* Only apply transformations if the buffers we
were given are newly copied from PA */
/* Only apply transformations if the buffers we were given are newly copied */
if (modified) {
size_t t;
size_t t, tm = 0;
struct gl_sampler_data d = {
.buf = buf, .sz = sz
};
bool set_opt = false; /* if gl->optimize_fft was set this frame */
for (t = 0; t < bind->t_sz; ++t) {
bind->transformations[t](gl, &gl->t_data[c], &d);
void (*apply)(struct gl_data*, void**, void*) = bind->transformations[t];
if (apply != NULL) {
if (gl->accel_fft) {
if (apply == transform_fft && !bind->optimize_fft) {
bind->optimize_fft = true;
set_opt = true;
tm = t;
} else {
/* Valid transformation after fft, no longer worth
pushing to the GPU. */
if (bind->optimize_fft) {
transform_fft(gl, &gl->t_data[c - 1], &d);
transform_gravity(gl, &gl->t_data[c + 1], &d);
transform_average(gl, &gl->t_data[c + 2], &d);
c += 2;
bind->optimize_fft = false;
set_opt = false;
}
apply(gl, &gl->t_data[c], &d);
}
} else {
apply(gl, &gl->t_data[c], &d);
if (apply == transform_fft) {
transform_gravity(gl, &gl->t_data[c + 1], &d);
transform_average(gl, &gl->t_data[c + 2], &d);
c += 2;
}
}
}
++c; /* Index for transformation data (note: change if new
transform types are added) */
}
if (set_opt) {
/* Force CPU interpolation off if we are pushing fft to the GPU,
as it requires the buffer data on system memory is updated with
transformation data (and is quite slow) */
if (old_interpolate)
gl->interpolate_glsl = true;
old_interpolate = false;
gl->interpolate = false;
/* Minor microptimization: truncate transforms if we're optimizing
the tailing FFT transform type, since we don't actually apply
them at this point. */
bind->t_sz = tm;
}
}
/* TODO: remove and replace with GLSL FFT */
if (bind->optimize_fft) {
transform_fft(gl, &gl->t_data[c],
&((struct gl_sampler_data) { .buf = buf, .sz = sz } ));
}
glActiveTexture(GL_TEXTURE0 + offset);
/* Update texture with our data */
update_1d_tex(tex, sz, gl->interpolate ? (ubuf ? ubuf : buf) : buf);
/* Apply audio-specific transformations in GLSL, if enabled */
if (bind->optimize_fft) {
struct sm_fb* av = &bind->av;
struct sm_fb* gr_store = &bind->gr_store;
struct gr_fb* gr = &bind->gr;
if (modified) {
if (gr->out == NULL) {
gr->out = calloc(gl->avg_frames, sizeof(struct sm_fb));
gr->out_sz = gl->avg_frames;
}
bind_1d_fbo(gr_store, sz);
/* Do the gravity storage computation with GL_MAX */
glUseProgram(gl->p_prog);
glActiveTexture(GL_TEXTURE0 + offset);
glBindTexture(GL_TEXTURE_1D, tex);
glUniform1i(gl->p_utex, offset);
if (gl->premultiply_alpha) glEnable(GL_BLEND);
glBlendEquation(GL_MAX);
glViewport(0, 0, sz, 1);
drawoverlay(&gl->overlay);
glViewport(0, 0, ww, wh);
glBlendEquation(GL_FUNC_ADD);
if (gl->premultiply_alpha) glDisable(GL_BLEND);
tex = gr_store->tex;
/* We are using this barrier extension so we can apply
transformations in-place using a single texture buffer.
Without this, we would need to double-buffer our textures
and perform pointless copies. */
glTextureBarrierNV();
/* Apply gravity */
glUseProgram(gl->gr_prog);
glActiveTexture(GL_TEXTURE0 + offset);
glBindTexture(GL_TEXTURE_1D, tex);
glUniform1i(gl->gr_utex, offset);
glUniform1f(gl->gr_udiff, gl->gravity_step * (1.0F / gl->ur));
if (!gl->premultiply_alpha) glDisable(GL_BLEND);
glViewport(0, 0, sz, 1);
drawoverlay(&gl->overlay);
glViewport(0, 0, ww, wh);
if (gl->avg_frames > 1) {
/* Write gravity buffer to output frames as if they are a
circular buffer. This prevents needless texture shifts */
struct sm_fb* out_frame = &gr->out[gr->out_idx];
bind_1d_fbo(out_frame, sz);
glUseProgram(gl->p_prog);
glActiveTexture(GL_TEXTURE0 + offset);
glBindTexture(GL_TEXTURE_1D, tex);
glUniform1i(gl->p_utex, offset);
glViewport(0, 0, sz, 1);
drawoverlay(&gl->overlay);
glViewport(0, 0, ww, wh);
/* Read circular buffer into averaging shader */
bind_1d_fbo(av, sz);
glUseProgram(gl->av_prog);
for (int t = 0; t < (int) gr->out_sz; ++t) {
GLuint c_off = offset + 1 + t;
glActiveTexture(GL_TEXTURE0 + c_off);
/* Textures are bound in descending order, such that
t0 is the most recent, and t[max - 1] is the last. */
int fr = gr->out_idx - t;
if (fr < 0)
fr = gr->out_sz + fr;
glBindTexture(GL_TEXTURE_1D, gr->out[fr].tex);
glUniform1i(gl->av_utex[t], c_off);
}
glViewport(0, 0, sz, 1);
drawoverlay(&gl->overlay);
glViewport(0, 0, ww, wh);
++gr->out_idx;
if (gr->out_idx >= gr->out_sz)
gr->out_idx = 0;
tex = av->tex;
}
if (!gl->premultiply_alpha) glEnable(GL_BLEND);
} else {
/* No audio buffer update; use last average result */
if (gl->avg_frames > 1)
tex = av->tex;
}
}
/* Apply pre-smoothing shader pass if configured */
if (audio && gl->smooth_pass) {
/* Compile preprocess shader and handle uniform locations */
if (!gl->sm_setup) {
gl->sm_utex = glGetUniformLocation(gl->sm_prog, "tex");
gl->sm_usz = glGetUniformLocation(gl->sm_prog, "sz");
gl->sm_uw = glGetUniformLocation(gl->sm_prog, "w");
glBindFragDataLocation(gl->sm_prog, 1, "fragment");
gl->sm_setup = true;
}
/* Allocate and setup our per-bind data, if needed */
struct sm_fb* sm = &bind->sm;
if (sm->tex == 0) {
glGenTextures(1, &sm->tex);
glGenFramebuffers(1, &sm->fbo);
/* 1D texture parameters */
glBindTexture(GL_TEXTURE_1D, sm->tex);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexImage1D(GL_TEXTURE_1D, 0, GL_R16, sz, 0, GL_RED, GL_FLOAT, NULL);
/* setup and bind framebuffer to texture */
glBindFramebuffer(GL_FRAMEBUFFER, sm->fbo);
glFramebufferTexture1D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,\
GL_TEXTURE_1D, sm->tex, 0);
switch (glCheckFramebufferStatus(GL_FRAMEBUFFER)) {
case GL_FRAMEBUFFER_COMPLETE: break;
default:
fprintf(stderr, "error in frambuffer state\n");
abort();
}
} else {
/* Just bind our data if it was already allocated and setup */
glBindFramebuffer(GL_FRAMEBUFFER, sm->fbo);
glBindTexture(GL_TEXTURE_1D, sm->tex);
}
bind_1d_fbo(sm, sz);
glUseProgram(gl->sm_prog);
glActiveTexture(GL_TEXTURE0 + offset);
@@ -2076,8 +2314,8 @@ bool rd_update(struct glava_renderer* r, float* lb, float* rb, size_t bsz, bool
}
glUniform1i(bind->uniform, 0);
break;
case SRC_AUDIO_L: handle_1d_tex(gl->audio_tex_l, lb, ilb, bsz, 1, true); break;
case SRC_AUDIO_R: handle_1d_tex(gl->audio_tex_r, rb, irb, bsz, 2, true); break;
case SRC_AUDIO_L: handle_audio(gl->audio_tex_l, lb, ilb, bsz, 1, true); break;
case SRC_AUDIO_R: handle_audio(gl->audio_tex_r, rb, irb, bsz, 2, true); break;
case SRC_AUDIO_SZ: glUniform1i(bind->uniform, bsz); break;
case SRC_SCREEN: glUniform2i(bind->uniform, (GLint) ww, (GLint) wh); break;
case SRC_TIME: glUniform1f(bind->uniform, (GLfloat) gl->time); break;
@@ -2212,7 +2450,7 @@ struct gl_wcb* rd_get_wcb (struct glava_renderer* r) { return r->gl->wc
void rd_destroy(struct glava_renderer* r) {
r->gl->wcb->destroy(r->gl->w);
if (r->gl->interpolate) free(r->gl->interpolate_buf[0]);
if (r->gl->interpolate_buf[0]) free(r->gl->interpolate_buf[0]);
size_t t, b;
if (r->gl->t_data) {
for (t = 0; t < r->gl->t_count; ++t) {
@@ -2226,11 +2464,15 @@ void rd_destroy(struct glava_renderer* r) {
for (b = 0; b < stage->binds_sz; ++b) {
struct gl_bind* bind = &stage->binds[b];
free(bind->transformations);
if (bind->gr.out != NULL)
free(bind->gr.out);
free((char*) bind->name); /* strdup */
}
free(stage->binds);
free((char*) stage->name); /* strdup */
}
if (r->gl->av_utex)
free(r->gl->av_utex);
free(r->gl->stages);
r->gl->wcb->terminate();
free(r->gl);

View File

@@ -14,6 +14,10 @@ if get_option('glad')
endif
endif
if get_option('buildtype').startswith('debug')
add_project_arguments('-DGLAVA_DEBUG', language: 'c')
endif
glava_dependencies = [
dependency('threads'),
cc.find_library('pulse'),

View File

@@ -128,7 +128,7 @@
This will delay data output by one update frame, so it can
desync audio with visual effects on low UPS configs. */
#request setinterpolate true
#request setinterpolate false
/* Frame limiter, set to the frames per second (FPS) desired or
simply set to zero (or lower) to disable the frame limiter. */
@@ -202,6 +202,14 @@
in the application generating the output. */
#request setsamplerate 22050
/* Enable GPU acceleration of the audio buffer's fourier transform.
This drastically reduces CPU usage, but should be avoided on
old integrated graphics hardware.
Enabling this also enables acceleration for post-FFT processing
effects, such as gravity, averaging, windowing, and interpolation. */
#request setaccelfft true
/* ** DEPRECATED **
Force window geometry (locking the window in place), useful
for some pesky WMs that try to reposition the window when

View File

@@ -0,0 +1,37 @@
out vec4 fragment;
in vec4 gl_FragCoord;
#include ":util/common.glsl"
/*
This averaging shader uses compile-time loop generation to ensure two things:
- We can avoid requiring GL 4.3 features to dynamically index texture arrays
- We ensure no branching occurs in this shader for optimial performance.
The alternative is requiring the GLSL compiler to determine that a loop for
texture array indexes (which must be determined at compile-time in 3.3) can be
expanded if the bounds are constant. This is somewhat vendor-specific so GLava
provides a special `#expand` macro to solve this problem in the preprocessing
stage.
*/
#define SAMPLER(I) uniform sampler1D t##I;
#expand SAMPLER _AVG_FRAMES
void main() {
float r = 0;
#define _AVG_WINDOW 0
#if _AVG_WINDOW == 0
#define F(I) r += texelFetch(t##I, int(gl_FragCoord.x), 0).r
#else
#define F(I) r += window(I, _AVG_FRAMES - 1) * texelFetch(t##I, int(gl_FragCoord.x), 0).r
#endif
#expand F _AVG_FRAMES
/* For some reason the CPU implementation of gravity/average produces the same output but
with half the amplitude. I can't figure it out right now, so I'm just halfing the results
here to ensure they are uniform.
TODO: fix this */
fragment.r = (r / _AVG_FRAMES) / 2;
}

View File

@@ -0,0 +1,21 @@
#ifndef _COMMON_GLSL
#define _COMMON_GLSL
#ifndef TWOPI
#define TWOPI 6.28318530718
#endif
#ifndef PI
#define PI 3.14159265359
#endif
/* Window value t that resides in range [0, sz] */
#define window(t, sz) (0.53836 - (0.46164 * cos(TWOPI * t / sz)))
/* Do nothing (used as an option for configuration) */
#define linear(x) (x)
/* Take value x that scales linearly between [0, 1) and return its sinusoidal curve */
#define sinusoidal(x) ((0.5 * sin((PI * (x)) - (PI / 2))) + 0.5)
/* Take value x that scales linearly between [0, 1) and return its circlar curve */
#define circular(x) sqrt(1 - (((x) - 1) * ((x) - 1)))
#endif

View File

@@ -0,0 +1,9 @@
uniform sampler1D tex;
uniform float diff;
out vec4 fragment;
in vec4 gl_FragCoord;
void main() {
fragment.r = texelFetch(tex, int(gl_FragCoord.x), 0).r - diff;
}

View File

@@ -1,27 +1,11 @@
#ifndef _SMOOTH_GLSL /* include gaurd */
#ifndef _SMOOTH_GLSL
#define _SMOOTH_GLSL
#ifndef TWOPI
#define TWOPI 6.28318530718
#endif
#ifndef PI
#define PI 3.14159265359
#endif
#include ":util/common.glsl"
#include "@smooth_parameters.glsl"
#include ":smooth_parameters.glsl"
/* window value t that resides in range [0, sz)*/
#define window(t, sz) (0.53836 - (0.46164 * cos(TWOPI * t / (sz - 1))))
/* this does nothing, but we keep it as an option for config */
#define linear(x) (x)
/* take value x that scales linearly between [0, 1) and return its sinusoidal curve */
#define sinusoidal(x) ((0.5 * sin((PI * (x)) - (PI / 2))) + 0.5)
/* take value x that scales linearly between [0, 1) and return its circlar curve */
#define circular(x) sqrt(1 - (((x) - 1) * ((x) - 1)))
#define average 0
#define maximum 1
#define hybrid 2