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}" GLAD_GEN="${1:-c}"
pushd glad 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 popd
cp glad/*.h glava/ cp glad/*.h glava/
cp glad/glad.c 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++ Language/Generator: C/C++
Specification: gl Specification: gl
@@ -8,15 +8,16 @@
Profile: compatibility Profile: compatibility
Extensions: Extensions:
GL_EXT_framebuffer_multisample, GL_EXT_framebuffer_multisample,
GL_EXT_texture_filter_anisotropic GL_EXT_texture_filter_anisotropic,
GL_NV_texture_barrier
Loader: True Loader: True
Local files: True Local files: True
Omit khrplatform: False Omit khrplatform: False
Commandline: 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: 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 #define GL_EXT_texture_filter_anisotropic 1
GLAPI int GLAD_GL_EXT_texture_filter_anisotropic; GLAPI int GLAD_GL_EXT_texture_filter_anisotropic;
#endif #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 #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" "The REQUEST argument is evaluated identically to the \'#request\' preprocessor directive\n"
"in GLSL files.\n" "in GLSL files.\n"
"\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" "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" "active configuration root (usually ~/.config/glava if present).\n"
"\n" "\n"
@@ -243,7 +240,8 @@ static struct option p_opts[] = {
}) })
/* Wait for glava_renderer target texture to be initialized and valid */ /* 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) { while(__atomic_load_n(ref, __ATOMIC_SEQ_CST) == NULL) {
/* Edge case: handle has not been assigned */ /* Edge case: handle has not been assigned */
struct timespec tv = { struct timespec tv = {
@@ -257,25 +255,29 @@ __attribute__((visibility("default"))) void glava_wait(glava_handle* ref) {
pthread_mutex_unlock(&(*ref)->lock); 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; return r->off_tex;
} }
/* Atomic size request */ /* 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 }; 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_store_n(&r->sizereq_flag, GLAVA_REQ_RESIZE, __ATOMIC_SEQ_CST);
} }
/* Atomic terminate request */ /* 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); glava_handle store = __atomic_exchange_n(ref, NULL, __ATOMIC_SEQ_CST);
if (store) if (store)
__atomic_store_n(&store->alive, false, __ATOMIC_SEQ_CST); __atomic_store_n(&store->alive, false, __ATOMIC_SEQ_CST);
} }
/* Atomic reload request */ /* 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); glava_handle store = __atomic_exchange_n(ref, NULL, __ATOMIC_SEQ_CST);
if (store) { if (store) {
__atomic_store_n(&reload, true, __ATOMIC_SEQ_CST); __atomic_store_n(&reload, true, __ATOMIC_SEQ_CST);
@@ -285,7 +287,8 @@ __attribute__((visibility("default"))) void glava_reload(glava_handle* ref) {
/* Main entry */ /* 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 */ /* Evaluate these macros only once, since they allocate */
const char const char

View File

@@ -13,6 +13,7 @@
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
#include "glava.h"
#include "render.h" #include "render.h"
#include "glsl_ext.h" #include "glsl_ext.h"
@@ -24,6 +25,7 @@
#define COLOR 5 #define COLOR 5
#define DEFINE 6 #define DEFINE 6
#define BIND 7 #define BIND 7
#define EXPAND 8
struct sbuf { struct sbuf {
char* buf; char* buf;
@@ -61,7 +63,7 @@ static void se_append(struct sbuf* sbuf, size_t elen, const char* fmt, ...) {
va_start(args, fmt); va_start(args, fmt);
int written; int written;
if ((written = vsnprintf(sbuf->buf + sbuf->at, space, fmt, args)) < 0) if ((written = vsnprintf(sbuf->buf + sbuf->at, space, fmt, args)) < 0)
abort(); glava_abort();
sbuf->at += written; sbuf->at += written;
va_end(args); 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, ...) \ #define parse_error(line, f, fmt, ...) \
do { \ do { \
fprintf(stderr, "[%s:%d] " fmt "\n", f, (int) line, __VA_ARGS__); \ fprintf(stderr, "[%s:%d] " fmt "\n", f, (int) line, __VA_ARGS__); \
abort(); \ glava_abort(); \
} while (0) } while (0)
#define parse_error_s(line, f, s) \ #define parse_error_s(line, f, s) \
do { \ do { \
fprintf(stderr, "[%s:%d] " s "\n", f, (int) line); \ fprintf(stderr, "[%s:%d] " s "\n", f, (int) line); \
abort(); \ glava_abort(); \
} while (0) } while (0)
struct schar { struct schar {
@@ -146,10 +148,11 @@ static struct schar directive(struct glsl_ext* ext, char** args,
if (args_sz == 0) { if (args_sz == 0) {
parse_error_s(line, f, "No arguments provided to #define directive!"); parse_error_s(line, f, "No arguments provided to #define directive!");
} }
size_t bsz = (strlen(args[0]) * 3) + 64; size_t bsz = (strlen(args[0]) * 3) + 64;
struct schar ret = { .buf = malloc(bsz) }; struct schar ret = { .buf = malloc(bsz) };
int r = snprintf(ret.buf, bsz, "#ifdef %1$s\n#undef %1$s\n#endif\n", args[0]); 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; ret.sz = r;
free_after(ext, ret.buf); free_after(ext, ret.buf);
return ret; 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); snprintf(path, sizeof(path) / sizeof(char), "%s/%s", ext->cd, target);
int fd = open(path, O_RDONLY); int fd = open(path, O_RDONLY);
if (fd == -1) { if (fd == -1)
parse_error(line, f, "failed to load GLSL shader source " parse_error(line, f, "failed to load GLSL shader source "
"specified by #include directive '%s': %s\n", "specified by #include directive '%s': %s\n",
path, strerror(errno)); path, strerror(errno));
}
struct stat st; struct stat st;
fstat(fd, &st); fstat(fd, &st);
char* map = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); 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 " parse_error(line, f, "failed to map GLSL shader source "
"specified by #include directive '%s': %s\n", "specified by #include directive '%s': %s\n",
path, strerror(errno)); path, strerror(errno));
}
struct glsl_ext next = { struct glsl_ext next = {
.source = map, .source = map,
@@ -205,7 +206,8 @@ static struct schar directive(struct glsl_ext* ext, char** args,
.handlers = ext->handlers, .handlers = ext->handlers,
.binds = ext->binds, .binds = ext->binds,
.ss_lookup = ext->ss_lookup, .ss_lookup = ext->ss_lookup,
.ss_len = ext->ss_len .ss_len = ext->ss_len,
.efuncs = ext->efuncs
}; };
/* recursively process */ /* recursively process */
@@ -238,11 +240,10 @@ static struct schar directive(struct glsl_ext* ext, char** args,
char c; char c;
size_t i; size_t i;
for (i = 0; (c = handler->fmt[i]) != '\0'; ++i) { for (i = 0; (c = handler->fmt[i]) != '\0'; ++i) {
if (args_sz <= 1 + i) { if (args_sz <= 1 + i)
parse_error(line, f, parse_error(line, f,
"failed to execute request '%s': expected format '%s'\n", "failed to execute request '%s': expected format '%s'\n",
request, handler->fmt); request, handler->fmt);
}
char* raw = args[1 + i]; char* raw = args[1 + i];
switch (c) { switch (c) {
case 'i': { case 'i': {
@@ -271,13 +272,12 @@ static struct schar directive(struct glsl_ext* ext, char** args,
case '1': { v = true; break; } case '1': { v = true; break; }
case '0': { v = false; break; } case '0': { v = false; break; }
default: default:
parse_error_s(line, f, parse_error_s(line, f, "tried to parse invalid "
"tried to parse invalid raw string into a boolean"); "raw string into a boolean");
} }
} else { } else
parse_error_s(line, f, parse_error_s(line, f, "tried to parse invalid "
"tried to parse invalid raw string into a boolean"); "raw string into a boolean");
}
processed_args[i] = malloc(sizeof(bool)); processed_args[i] = malloc(sizeof(bool));
*(bool*) processed_args[i] = v; *(bool*) processed_args[i] = v;
break; break;
@@ -293,11 +293,51 @@ static struct schar directive(struct glsl_ext* ext, char** args,
free(processed_args); free(processed_args);
} }
} }
if (!found) { if (!found)
parse_error(line, f, "unknown request type '%s'", request); 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 }; 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; size_t args_sz = 0;
bool prev_slash = false, comment = false, comment_line = false, prev_asterix = false, 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); 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; goto copy;
case '#': { case '#': {
/* handle hex color syntax */ /* 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; state = COLOR;
cbuf_idx = 0; cbuf_idx = 0;
continue; continue;
} else goto normal_char; } else {
skip_color_start = false;
goto normal_char;
}
} }
case '@': { case '@': {
/* handle bind syntax */ /* 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 /* To emit the default, we push back the cursor to where it starts
and simply resume parsing from a normal context. */ and simply resume parsing from a normal context. */
t = b_restart; 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); " Try assigning a default or binding the value.", parsed_name);
} }
state = GLSL; state = GLSL;
@@ -566,7 +614,8 @@ void ext_process(struct glsl_ext* ext, const char* f) {
DIRECTIVE_CASE(request, REQUEST); DIRECTIVE_CASE(request, REQUEST);
DIRECTIVE_CASE(include, INCLUDE); DIRECTIVE_CASE(include, INCLUDE);
DIRECTIVE_CASE(define, DEFINE); DIRECTIVE_CASE(define, DEFINE);
DIRECTIVE_CASE(expand, EXPAND);
/* no match */ /* no match */
if (state == MACRO) skip_macro(); if (state == MACRO) skip_macro();
#undef DIRECTIVE_CMP #undef DIRECTIVE_CMP
@@ -579,10 +628,15 @@ void ext_process(struct glsl_ext* ext, const char* f) {
*args = NULL; *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':
case 'A' ... 'Z': case 'A' ... 'Z':
continue; continue;
default: default:
macro_parse_error:
/* invalid char, malformed! */ /* invalid char, malformed! */
parse_error(line, f, "Unexpected character '%c' while parsing GLSL directive", at); 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) } } while (0)
case REQUEST: case REQUEST:
case INCLUDE: case INCLUDE:
case DEFINE: { case DEFINE:
case EXPAND: {
switch (at) { switch (at) {
case ' ': case ' ':
case '\t': case '\t':

View File

@@ -28,18 +28,30 @@ struct request_handler {
#endif #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 { struct glsl_ext {
char* processed; /* OUT: null terminated processed source */ char* processed; /* OUT: null terminated processed source */
size_t p_len; /* OUT: length of processed buffer, excluding null char */ size_t p_len; /* OUT: length of processed buffer, excluding null char */
const char* source; /* IN: raw data passed via ext_process */ const char* source; /* IN: raw data passed via ext_process */
size_t source_len; /* IN: raw source len */ size_t source_len; /* IN: raw source len */
const char* cd; /* IN: current directory */ const char* cd; /* IN: current directory */
const char* cfd; /* IN: config directory, if NULL it is assumed to cd */ const char* cfd; /* IN: config directory, if NULL it is assumed to cd */
const char* dd; /* IN: default directory */ const char* dd; /* IN: default directory */
struct rd_bind* binds; /* OPT IN: --pipe binds */ struct rd_bind* binds; /* OPT IN: --pipe binds */
void** destruct; /* internal */ struct glsl_ext_efunc* efuncs; /* OPT IN: `#expand` binds */
size_t destruct_sz; /* internal */ void** destruct; /* internal */
char** ss_lookup; /* source-string lookup table */ size_t destruct_sz; /* internal */
char** ss_lookup; /* source-string lookup table */
size_t* ss_len; size_t* ss_len;
size_t ss_len_s; size_t ss_len_s;
bool ss_own; bool ss_own;

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 TWOPI 6.28318530718
#define PI 3.14159265359 #define PI 3.14159265359
#define swap(a, b) do { __auto_type tmp = a; a = b; b = tmp; } while (0) #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_LEFT 4
#define IB_WORK_RIGHT 5 #define IB_WORK_RIGHT 5
/* Only a single vertex shader is needed for GLava, since all rendering is done in the fragment shader /* Only a single vertex shader is needed, since all rendering
over a fullscreen quad */ is done in the fragment shader over a fullscreen quad */
#define VERTEX_SHADER_SRC \ #define VERTEX_SHADER_SRC \
"layout(location = 0) in vec3 pos; void main() { gl_Position = vec4(pos.x, pos.y, 0.0F, 1.0F); }" "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; bool glad_instantiated = false;
struct gl_wcb* wcbs[2] = {}; struct gl_wcb* wcbs[2] = {};
static size_t wcbs_idx = 0; static size_t wcbs_idx = 0;
@@ -68,27 +101,37 @@ struct gl_bind_src {
int src_type; int src_type;
}; };
/* function that can be applied to uniform binds */ /* Function that can be applied to uniform binds */
struct gl_transform { struct gl_transform {
const char* name; const char* name;
int type; int type;
void (*apply)(struct gl_data*, void**, void* data); 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 { struct gl_sampler_data {
float* buf; float* buf;
size_t sz; 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 { struct sm_fb {
GLuint fbo, tex; 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 */ /* GLSL uniform bind */
struct gl_bind { struct gl_bind {
@@ -98,7 +141,9 @@ struct gl_bind {
int src_type; int src_type;
void (**transformations)(struct gl_data*, void**, void* data); void (**transformations)(struct gl_data*, void**, void* data);
size_t t_sz; 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 */ /* GL screen framebuffer object */
@@ -121,18 +166,18 @@ struct overlay_data {
struct gl_data { struct gl_data {
struct gl_sfbo* stages; struct gl_sfbo* stages;
struct overlay_data overlay; 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; size_t stages_sz, bufscale, avg_frames;
void* w; void* w;
struct gl_wcb* wcb; struct gl_wcb* wcb;
int lww, lwh, lwx, lwy; /* last window dimensions */ int lww, lwh, lwx, lwy; /* last window dimensions */
int rate; /* framerate */ int rate; /* framerate */
double tcounter; double tcounter;
float time, timecycle; float time, timecycle;
int fcounter, ucounter, kcounter; int fcounter, ucounter, kcounter;
bool print_fps, avg_window, interpolate, force_geometry, force_raised, bool print_fps, avg_window, interpolate, interpolate_glsl, force_geometry,
copy_desktop, smooth_pass, premultiply_alpha, check_fullscreen, force_raised, copy_desktop, smooth_pass, premultiply_alpha, check_fullscreen,
clickthrough, mirror_input; clickthrough, mirror_input, accel_fft;
void** t_data; void** t_data;
size_t t_count; size_t t_count;
float gravity_step, target_spu, fr, ur, smooth_distance, smooth_ratio, float gravity_step, target_spu, fr, ur, smooth_distance, smooth_ratio,
@@ -146,8 +191,10 @@ struct gl_data {
struct rd_bind* binds; struct rd_bind* binds;
GLuint bg_prog, bg_utex, bg_screen; GLuint bg_prog, bg_utex, bg_screen;
bool bg_setup; bool bg_setup;
GLuint sm_utex, sm_usz, sm_uw; GLuint sm_utex, sm_usz, sm_uw,
bool sm_setup; gr_utex, gr_udiff,
p_utex;
GLuint* av_utex;
bool test_mode; bool test_mode;
struct gl_sfbo off_sfbo; struct gl_sfbo off_sfbo;
#ifdef GLAVA_DEBUG #ifdef GLAVA_DEBUG
@@ -178,7 +225,7 @@ static GLuint shaderload(const char* rpath,
size_t s_len = strlen(shader); 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]; char path[raw ? 2 : strlen(rpath) + s_len + 2];
if (raw) { if (raw) {
path[0] = '*'; path[0] = '*';
@@ -186,10 +233,8 @@ static GLuint shaderload(const char* rpath,
} }
struct stat st; struct stat st;
int fd = -1; int fd = -1;
if (!raw) { if (!raw) {
snprintf(path, sizeof(path) / sizeof(char), "%s/%s", shader, rpath); snprintf(path, sizeof(path) / sizeof(char), "%s/%s", shader, rpath);
fd = open(path, O_RDONLY); fd = open(path, O_RDONLY);
if (fd == -1) { if (fd == -1) {
fprintf(stderr, "failed to load shader '%s': %s\n", path, strerror(errno)); fprintf(stderr, "failed to load shader '%s': %s\n", path, strerror(errno));
@@ -197,8 +242,7 @@ static GLuint shaderload(const char* rpath,
} }
fstat(fd, &st); fstat(fd, &st);
} }
/* open and create a copy with prepended header */
GLint max_uniforms; GLint max_uniforms;
glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &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"; 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) { for (struct rd_bind* bd = gl->binds; bd->name != NULL; ++bd) {
size_t inc = snprintf(NULL, 0, fmt, bd->stype, bd->name); size_t inc = snprintf(NULL, 0, fmt, bd->stype, bd->name);
bind_header = realloc(bind_header, bh_idx + inc + 1); bind_header = realloc(bind_header, bh_idx + inc + 1);
@@ -217,19 +262,38 @@ static GLuint shaderload(const char* rpath,
bh_idx += inc; bh_idx += inc;
} }
static const GLchar* header_fmt = /* Append to header entries with a #define for each `#expand` control */
"#version %d\n" MUTABLE char* efmt_header = malloc(1);
"#define _UNIFORM_LIMIT %d\n" MUTABLE size_t efmt_idx = 0;
"#define _PRE_SMOOTHED_AUDIO %d\n" INLINE(void, append_efmt)(const char* n, size_t v) {
"#define _SMOOTH_FACTOR %.6f\n" size_t inc = snprintf(NULL, 0, "#define %s %d\n", n, (int) v);
"#define _USE_ALPHA %d\n" efmt_header = realloc(efmt_header, efmt_idx + inc + 1);
"#define _PREMULTIPLY_ALPHA %d\n" snprintf(efmt_header + efmt_idx, inc + 1, "#define %s %d\n", n, (int) v);
"#define _CHANNELS %d\n" efmt_idx += inc;
"#define USE_STDIN %d\n" };
"#if USE_STDIN == 1\n"
"uniform %s STDIN;\n" /* Create `#expand` header entry, using the above closure */
"#endif\n" #define EBIND(n, v) \
"%s"; ({ \
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 = { struct glsl_ext ext = {
.source = raw ? NULL : map, .source = raw ? NULL : map,
@@ -240,19 +304,27 @@ static GLuint shaderload(const char* rpath,
.handlers = handlers, .handlers = handlers,
.processed = (char*) (raw ? shader : NULL), .processed = (char*) (raw ? shader : NULL),
.p_len = raw ? s_len : 0, .p_len = raw ? s_len : 0,
.binds = gl->binds .binds = gl->binds,
.efuncs = efuncs
}; };
/* If this is raw input, skip processing */ /* If this is raw input, skip processing */
if (!raw) ext_process(&ext, rpath); if (!raw) ext_process(&ext, rpath);
/* 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) + 64; size_t blen = strlen(header_fmt) + 32 + pad;
GLchar* buf = malloc((blen * sizeof(GLchar*)) + ext.p_len); GLchar* buf = malloc((blen * sizeof(GLchar*)) + ext.p_len);
int written = snprintf(buf, blen, header_fmt, (int) shader_version, (int) max_uniforms, int written = snprintf(buf, blen, header_fmt, (int) shader_version,
gl->smooth_pass ? 1 : 0, (double) gl->smooth_factor, (double) gl->smooth_factor, gl->stdin_type != STDIN_TYPE_NONE,
1, gl->premultiply_alpha ? 1 : 0, gl->mirror_input ? 1 : 2, bind_types[gl->stdin_type].n, bind_header, efmt_header);
gl->stdin_type != STDIN_TYPE_NONE, bind_types[gl->stdin_type].n,
bind_header);
if (written < 0) { if (written < 0) {
fprintf(stderr, "snprintf() encoding error while prepending header to shader '%s'\n", path); fprintf(stderr, "snprintf() encoding error while prepending header to shader '%s'\n", path);
return 0; return 0;
@@ -266,11 +338,11 @@ static GLuint shaderload(const char* rpath,
switch (glGetError()) { switch (glGetError()) {
case GL_INVALID_VALUE: case GL_INVALID_VALUE:
fprintf(stderr, "invalid value while loading shader source\n"); fprintf(stderr, "invalid value while loading shader source\n");
abort(); //todo: remove glava_abort();
return 0; return 0;
case GL_INVALID_OPERATION: case GL_INVALID_OPERATION:
fprintf(stderr, "invalid operation while loading shader source\n"); fprintf(stderr, "invalid operation while loading shader source\n");
abort(); //todo: remove glava_abort();
return 0; return 0;
default: {} default: {}
} }
@@ -418,11 +490,6 @@ static GLuint shaderbuild_f(struct gl_data* gl,
return 0; return 0;
} }
} else if (!strcmp(path + t + 1, "vert")) { } 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); fprintf(stderr, "shaderbuild(): vertex shaders not allowed: %s\n", path);
abort(); abort();
} else { } else {
@@ -434,7 +501,8 @@ static GLuint shaderbuild_f(struct gl_data* gl,
} }
} }
/* load builtin vertex shader */ /* 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); fflush(stdout);
return shaderlink_f(shaders); return shaderlink_f(shaders);
} }
@@ -538,12 +606,12 @@ struct err_msg {
#define CODE(c) .code = c, .cname = #c #define CODE(c) .code = c, .cname = #c
static const struct err_msg err_lookup[] = { static const struct err_msg err_lookup[] = {
{ CODE(GL_INVALID_ENUM), .msg = "Invalid enum parameter" }, { CODE(GL_INVALID_ENUM), .msg = "Invalid enum parameter" },
{ CODE(GL_INVALID_VALUE), .msg = "Invalid value parameter" }, { CODE(GL_INVALID_VALUE), .msg = "Invalid value parameter" },
{ CODE(GL_INVALID_OPERATION), .msg = "Invalid operation" }, { CODE(GL_INVALID_OPERATION), .msg = "Invalid operation" },
{ CODE(GL_STACK_OVERFLOW), .msg = "Stack overflow" }, { CODE(GL_STACK_OVERFLOW), .msg = "Stack overflow" },
{ CODE(GL_STACK_UNDERFLOW), .msg = "Stack underflow" }, { CODE(GL_STACK_UNDERFLOW), .msg = "Stack underflow" },
{ CODE(GL_OUT_OF_MEMORY), .msg = "Out of memory" }, { CODE(GL_OUT_OF_MEMORY), .msg = "Out of memory" },
{ CODE(GL_INVALID_FRAMEBUFFER_OPERATION), .msg = "Out of memory" }, { CODE(GL_INVALID_FRAMEBUFFER_OPERATION), .msg = "Out of memory" },
#ifdef GL_CONTEXT_LOSS #ifdef GL_CONTEXT_LOSS
{ CODE(GL_CONTEXT_LOSS), .msg = "Context loss (graphics device or driver reset?)" } { CODE(GL_CONTEXT_LOSS), .msg = "Context loss (graphics device or driver reset?)" }
@@ -557,8 +625,7 @@ static void glad_debugcb(const char* name, void *funcptr, int len_args, ...) {
if (err != GL_NO_ERROR) { if (err != GL_NO_ERROR) {
const char* cname = "?", * msg = "Unknown error code"; const char* cname = "?", * msg = "Unknown error code";
size_t t; for (size_t t = 0; t < sizeof(err_lookup) / sizeof(struct err_msg); ++t) {
for (t = 0; t < sizeof(err_lookup) / sizeof(struct err_msg); ++t) {
if (err_lookup[t].code == err) { if (err_lookup[t].code == err) {
cname = err_lookup[t].cname; cname = err_lookup[t].cname;
msg = err_lookup[t].msg; 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 } { .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) \ #define ALLOC_ONCE(u, udata, sz) \
if (*udata == NULL) { \ if (*udata == NULL) { \
u = calloc(sz, sizeof(typeof(*u))); \ 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 /* Calculate real indexes for sampling at this position, since the
distance is specified in scalar values */ distance is specified in scalar values */
int smin = (int) floor(powf(E, max(db - d->smooth_distance, 0))); 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; int count = 0;
for (int s = smin; s <= smax; ++s) { for (int s = smin; s <= smax; ++s) {
if (b[s]) { if (b[s]) {
@@ -656,7 +723,7 @@ void transform_gravity(struct gl_data* d, void** udata, void* data) {
float* applied; float* applied;
ALLOC_ONCE(applied, udata, sz); ALLOC_ONCE(applied, udata, sz);
float g = d->gravity_step * (1.0F / d->ur); float g = d->gravity_step * (1.0F / d->ur);
for (t = 0; t < sz; ++t) { for (t = 0; t < sz; ++t) {
@@ -678,7 +745,8 @@ void transform_average(struct gl_data* d, void** udata, void* data) {
float* bufs; float* bufs;
ALLOC_ONCE(bufs, udata, tsz); ALLOC_ONCE(bufs, udata, tsz);
/* TODO: optimize into circle buffer */
memmove(bufs, &bufs[sz], (tsz - sz) * sizeof(float)); memmove(bufs, &bufs[sz], (tsz - sz) * sizeof(float));
memcpy(&bufs[tsz - sz], b, 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) } while (0)
if (use_window) if (use_window)
DO_AVG(window(f, d->avg_frames)); DO_AVG(window(f, d->avg_frames - 1));
else else
DO_AVG(1); 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) { void transform_fft(struct gl_data* d, void** _, void* in) {
struct gl_sampler_data* s = (struct gl_sampler_data*) in; struct gl_sampler_data* s = (struct gl_sampler_data*) in;
float* data = s->buf; float* data = s->buf;
@@ -729,7 +787,12 @@ void transform_fft(struct gl_data* d, void** _, void* in) {
unsigned long n, mmax, m, j, istep, i; unsigned long n, mmax, m, j, istep, i;
float wtemp, wr, wpr, wpi, wi, theta; float wtemp, wr, wpr, wpi, wi, theta;
float tempr, tempi; float tempr, tempi;
/* apply window */
for (i = 0; i < s->sz; ++i) {
data[i] *= window(i, s->sz - 1);
}
/* reverse-binary reindexing */ /* reverse-binary reindexing */
n = nn << 1; n = nn << 1;
j = 1; j = 1;
@@ -745,7 +808,7 @@ void transform_fft(struct gl_data* d, void** _, void* in) {
} }
j += m; j += m;
}; };
/* here begins the Danielson-Lanczos section */ /* here begins the Danielson-Lanczos section */
mmax = 2; mmax = 2;
while (n > mmax) { while (n > mmax) {
@@ -783,17 +846,16 @@ void transform_fft(struct gl_data* d, void** _, void* in) {
} }
static struct gl_transform transform_functions[] = { 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 = "fft", .type = BIND_SAMPLER1D, .apply = transform_fft },
{ .name = "wrange", .type = BIND_SAMPLER1D, .apply = transform_wrange }, { .name = "wrange", .type = BIND_SAMPLER1D, .apply = transform_wrange },
{ .name = "avg", .type = BIND_SAMPLER1D, .apply = transform_average }, { .name = "avg", .type = BIND_SAMPLER1D, .apply = NULL },
{ .name = "gravity", .type = BIND_SAMPLER1D, .apply = transform_gravity }, { .name = "gravity", .type = BIND_SAMPLER1D, .apply = NULL },
{ .name = "smooth", .type = BIND_SAMPLER1D, .apply = transform_smooth } { .name = "smooth", .type = BIND_SAMPLER1D, .apply = transform_smooth }
}; };
static struct gl_bind_src* lookup_bind_src(const char* str) { static struct gl_bind_src* lookup_bind_src(const char* str) {
size_t t; for (size_t t = 0; t < sizeof(bind_sources) / sizeof(struct gl_bind_src); ++t) {
for (t = 0; t < sizeof(bind_sources) / sizeof(struct gl_bind_src); ++t) {
if (!strcmp(bind_sources[t].name, str)) { if (!strcmp(bind_sources[t].name, str)) {
return &bind_sources[t]; return &bind_sources[t];
} }
@@ -801,25 +863,6 @@ static struct gl_bind_src* lookup_bind_src(const char* str) {
return NULL; 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, struct glava_renderer* rd_new(const char** paths, const char* entry,
const char** requests, const char* force_backend, const char** requests, const char* force_backend,
struct rd_bind* bindings, int stdin_type, 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, .avg_window = true,
.gravity_step = 4.2, .gravity_step = 4.2,
.interpolate = true, .interpolate = true,
.interpolate_glsl = false,
.force_geometry = false, .force_geometry = false,
.force_raised = false, .force_raised = false,
.smooth_factor = 0.025, .smooth_factor = 0.025,
@@ -873,20 +917,24 @@ struct glava_renderer* rd_new(const char** paths, const char* entry,
.smooth_ratio = 4, .smooth_ratio = 4,
.bg_tex = 0, .bg_tex = 0,
.sm_prog = 0, .sm_prog = 0,
.av_prog = 0,
.gr_prog = 0,
.p_prog = 0,
.copy_desktop = true, .copy_desktop = true,
.premultiply_alpha = true, .premultiply_alpha = true,
.mirror_input = false, .mirror_input = false,
.accel_fft = true,
.check_fullscreen = false, .check_fullscreen = false,
.smooth_pass = true, .smooth_pass = true,
.fft_scale = 10.2F, .fft_scale = 10.2F,
.fft_cutoff = 0.3F, .fft_cutoff = 0.3F,
.geometry = { 0, 0, 500, 400 }, .geometry = { 0, 0, 500, 400 },
.clear_color = { 0.0F, 0.0F, 0.0F, 0.0F }, .clear_color = { 0.0F, 0.0F, 0.0F, 0.0F },
.interpolate_buf = { [0] = NULL },
.clickthrough = false, .clickthrough = false,
.stdin_type = stdin_type, .stdin_type = stdin_type,
.binds = bindings, .binds = bindings,
.bg_setup = false, .bg_setup = false,
.sm_setup = false,
.test_mode = test_mode, .test_mode = test_mode,
.off_sfbo = { .off_sfbo = {
.name = "test", .name = "test",
@@ -951,7 +999,7 @@ struct glava_renderer* rd_new(const char** paths, const char* entry,
if (!gl->wcb) { if (!gl->wcb) {
fprintf(stderr, "Invalid window creation backend selected: '%s'\n", backend); fprintf(stderr, "Invalid window creation backend selected: '%s'\n", backend);
abort(); glava_abort();
} }
#ifdef GLAD_DEBUG #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]; }) }, .handler = RHANDLER(name, args, { r->rate_request = *(int*) args[0]; }) },
{ .name = "setsamplesize", .fmt = "i", { .name = "setsamplesize", .fmt = "i",
.handler = RHANDLER(name, args, { r->samplesize_request = *(int*) args[0]; }) }, .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", { .name = "setavgframes", .fmt = "i",
.handler = RHANDLER(name, args, { .handler = RHANDLER(name, args, {
if (!loading_smooth_pass) gl->avg_frames = *(int*) args[0]; }) }, 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*))); realloc(bind->transformations, bind->t_sz * sizeof(void (*)(void*)));
bind->transformations[bind->t_sz - 1] = tran->apply; bind->transformations[bind->t_sz - 1] = tran->apply;
++t_count; ++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", { .name = "uniform", .fmt = "ss",
@@ -1226,7 +1297,8 @@ struct glava_renderer* rd_new(const char** paths, const char* entry,
.src_type = src->src_type, .src_type = src->src_type,
.transformations = malloc(1), .transformations = malloc(1),
.t_sz = 0, .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; current = s;
bool skip; 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); if (skip && verbose) printf("disabled: '%s'\n", d->d_name);
/* check for compilation failure */ /* check for compilation failure */
if (!id && !skip) if (!id && !skip)
@@ -1539,8 +1612,8 @@ struct glava_renderer* rd_new(const char** paths, const char* entry,
/* Use dirct rendering on final pass */ /* Use dirct rendering on final pass */
if (final) final->indirect = false; if (final) final->indirect = false;
} }
/* Compile smooth pass shader */ /* Compile various audio processing shaders */
{ {
const char* util_folder = "util"; const char* util_folder = "util";
@@ -1548,11 +1621,46 @@ struct glava_renderer* rd_new(const char** paths, const char* entry,
size_t usz = d_len + u_len + 2; size_t usz = d_len + u_len + 2;
char util[usz]; /* module pack path to use */ char util[usz]; /* module pack path to use */
snprintf(util, usz, "%s/%s", data, util_folder); snprintf(util, usz, "%s/%s", data, util_folder);
/* Compile smooth pass shader */
loading_smooth_pass = true; loading_smooth_pass = true;
if (!(gl->sm_prog = shaderbuild(gl, util, data, dd, handlers, shader_version, NULL, "smooth_pass.frag"))) if (!(gl->sm_prog = shaderbuild(gl, util, data, dd, handlers, shader_version,
abort(); 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; 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 */ /* target seconds per update */
gl->target_spu = (float) (r->samplesize_request / 4) / (float) r->rate_request; gl->target_spu = (float) (r->samplesize_request / 4) / (float) r->rate_request;
@@ -1572,14 +1680,11 @@ struct glava_renderer* rd_new(const char** paths, const char* entry,
gl->interpolate_buf[IB_WORK_LEFT ] = &ibuf[isz * IB_WORK_LEFT ]; /* left interpolation results */ gl->interpolate_buf[IB_WORK_LEFT ] = &ibuf[isz * IB_WORK_LEFT ]; /* left interpolation results */
gl->interpolate_buf[IB_WORK_RIGHT ] = &ibuf[isz * IB_WORK_RIGHT ]; /* right interpolation results */ gl->interpolate_buf[IB_WORK_RIGHT ] = &ibuf[isz * IB_WORK_RIGHT ]; /* right interpolation results */
} }
{ gl->t_data = malloc(sizeof(void*) * t_count);
gl->t_data = malloc(sizeof(void*) * t_count); gl->t_count = t_count;
gl->t_count = t_count; for (size_t t = 0; t < t_count; ++t) {
size_t t; gl->t_data[t] = NULL;
for (t = 0; t < t_count; ++t) {
gl->t_data[t] = NULL;
}
} }
overlay(&gl->overlay); overlay(&gl->overlay);
@@ -1591,6 +1696,37 @@ struct glava_renderer* rd_new(const char** paths, const char* entry,
return r; 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) { void rd_time(struct glava_renderer* r) {
struct gl_data* gl = r->gl; 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 */ /* 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 */ 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; gl->interpolate = uratio <= 0.9F ? old_interpolate : false;
/* Perform buffer scaling */ /* Perform buffer scaling */
@@ -1647,10 +1783,10 @@ bool rd_update(struct glava_renderer* r, float* lb, float* rb, size_t bsz, bool
} }
/* Linear interpolation */ /* Linear interpolation */
float float * ilb = NULL, * irb = NULL;
* ilb = gl->interpolate_buf[IB_WORK_LEFT ],
* irb = gl->interpolate_buf[IB_WORK_RIGHT];
if (gl->interpolate) { if (gl->interpolate) {
ilb = gl->interpolate_buf[IB_WORK_LEFT ];
irb = gl->interpolate_buf[IB_WORK_RIGHT];
for (t = 0; t < bsz; ++t) { for (t = 0; t < bsz; ++t) {
/* Obtain start/end values at this index for left & right buffers */ /* Obtain start/end values at this index for left & right buffers */
float 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 */ /* 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); 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); glClear(GL_COLOR_BUFFER_BIT);
if (!current->indirect && gl->copy_desktop) { 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 = static const char* frag_shader =
"uniform sampler2D tex;" "\n" "uniform sampler2D tex;" "\n"
"uniform ivec2 screen;" "\n" "uniform ivec2 screen;" "\n"
@@ -1962,76 +2101,175 @@ bool rd_update(struct glava_renderer* r, float* lb, float* rb, size_t bsz, bool
MUTABLE size_t b, c = 0; MUTABLE size_t b, c = 0;
for (b = 0; b < current->binds_sz; ++b) { for (b = 0; b < current->binds_sz; ++b) {
struct gl_bind* bind = &current->binds[b]; struct gl_bind* bind = &current->binds[b];
/* Handle transformations and bindings for 1D samplers */ /* 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) { size_t sz, int offset, bool audio) {
if (load_flags[offset]) if (load_flags[offset])
goto bind_uniform; goto bind_uniform;
load_flags[offset] = true; load_flags[offset] = true;
/* Only apply transformations if the buffers we /* Only apply transformations if the buffers we were given are newly copied */
were given are newly copied from PA */
if (modified) { if (modified) {
size_t t; size_t t, tm = 0;
struct gl_sampler_data d = { struct gl_sampler_data d = {
.buf = buf, .sz = sz .buf = buf, .sz = sz
}; };
bool set_opt = false; /* if gl->optimize_fft was set this frame */
for (t = 0; t < bind->t_sz; ++t) { 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 ++c; /* Index for transformation data (note: change if new
transform types are added) */ 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); glActiveTexture(GL_TEXTURE0 + offset);
/* Update texture with our data */ /* Update texture with our data */
update_1d_tex(tex, sz, gl->interpolate ? (ubuf ? ubuf : buf) : buf); 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 */ /* Apply pre-smoothing shader pass if configured */
if (audio && gl->smooth_pass) { 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 */ /* Allocate and setup our per-bind data, if needed */
struct sm_fb* sm = &bind->sm; struct sm_fb* sm = &bind->sm;
if (sm->tex == 0) { bind_1d_fbo(sm, sz);
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);
}
glUseProgram(gl->sm_prog); glUseProgram(gl->sm_prog);
glActiveTexture(GL_TEXTURE0 + offset); 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); glUniform1i(bind->uniform, 0);
break; break;
case SRC_AUDIO_L: handle_1d_tex(gl->audio_tex_l, lb, ilb, bsz, 1, true); break; case SRC_AUDIO_L: handle_audio(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_R: handle_audio(gl->audio_tex_r, rb, irb, bsz, 2, true); break;
case SRC_AUDIO_SZ: glUniform1i(bind->uniform, bsz); break; case SRC_AUDIO_SZ: glUniform1i(bind->uniform, bsz); break;
case SRC_SCREEN: glUniform2i(bind->uniform, (GLint) ww, (GLint) wh); break; case SRC_SCREEN: glUniform2i(bind->uniform, (GLint) ww, (GLint) wh); break;
case SRC_TIME: glUniform1f(bind->uniform, (GLfloat) gl->time); 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) { void rd_destroy(struct glava_renderer* r) {
r->gl->wcb->destroy(r->gl->w); 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; size_t t, b;
if (r->gl->t_data) { if (r->gl->t_data) {
for (t = 0; t < r->gl->t_count; ++t) { 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) { for (b = 0; b < stage->binds_sz; ++b) {
struct gl_bind* bind = &stage->binds[b]; struct gl_bind* bind = &stage->binds[b];
free(bind->transformations); free(bind->transformations);
if (bind->gr.out != NULL)
free(bind->gr.out);
free((char*) bind->name); /* strdup */ free((char*) bind->name); /* strdup */
} }
free(stage->binds); free(stage->binds);
free((char*) stage->name); /* strdup */ free((char*) stage->name); /* strdup */
} }
if (r->gl->av_utex)
free(r->gl->av_utex);
free(r->gl->stages); free(r->gl->stages);
r->gl->wcb->terminate(); r->gl->wcb->terminate();
free(r->gl); free(r->gl);

View File

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

View File

@@ -128,7 +128,7 @@
This will delay data output by one update frame, so it can This will delay data output by one update frame, so it can
desync audio with visual effects on low UPS configs. */ 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 /* Frame limiter, set to the frames per second (FPS) desired or
simply set to zero (or lower) to disable the frame limiter. */ simply set to zero (or lower) to disable the frame limiter. */
@@ -202,6 +202,14 @@
in the application generating the output. */ in the application generating the output. */
#request setsamplerate 22050 #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 ** /* ** DEPRECATED **
Force window geometry (locking the window in place), useful Force window geometry (locking the window in place), useful
for some pesky WMs that try to reposition the window when 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
#ifndef _SMOOTH_GLSL /* include gaurd */
#define _SMOOTH_GLSL #define _SMOOTH_GLSL
#ifndef TWOPI #include ":util/common.glsl"
#define TWOPI 6.28318530718
#endif
#ifndef PI
#define PI 3.14159265359
#endif
#include "@smooth_parameters.glsl" #include "@smooth_parameters.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 average 0
#define maximum 1 #define maximum 1
#define hybrid 2 #define hybrid 2